一、线程简介
程序、进程和线程的区别:
-
程序:指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
-
进程:执行程序的一次执行过程,是系统资源分配的单位,是一个动态的概念。
-
线程:CPU调度和执行的单位,一个进程中可以包含多个线程。
注意:真正多线程表示有多个CPU,即多核,例如服务器;如果只有一个CPU那只能算模拟多线程,因为在一个时间点里只能执行一个进程,因为进程切换速度快,给人多线程同时执行的错觉。
注意:线程不一定立即执行,需要CPU安排调度。
二、线程创建
创建线程的三种方式:继承Thread、实现Runnable接口、实现Callable接口
继承Thread
- 编程思路
// 1.自定义线程类继承Thread类
public class ThreadDemo extends Thread{}
// 2.重写run()方法,编写线程执行体
@Override
public void run() {...}
// 3.创建线程对象,调用start()方法启动线程
new ThreadDemo().start();
不建议使用:避免OOP单继承局限性。
实现Runnable接口
- 编程思路
// 定义线程类并实现Runnable接口
public class ThreadDemo implements Runnable{}
// 实现run()方法,编写线程执行体
@Override
public void run() {...}
// 创建线程对象,调用start()方法启动线程
new Thread(new ThreadDemo()).start();
推荐使用:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用
实现Callable接口
- 编程思路
// 1.实现Callable接口,需要返回值类型
public class ThreadDemo implements Callable<String>{}
// 2.重写call方法,需要抛出异常
@Override
public String call() {...}
// 3.创建目标对象
ThreadDemo callThread = new ThreadDemo();
// 4.创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
// 5.提交执行
Future<String> result = ser.submit(callThread);
// 6.获取结果
String strResult = resule.get();
// 7.关闭服务
ser.shutdownNow();
使用区别:可以定义返回值,可以抛出异常。
三、线程状态
线程五大状态:创建、就绪、运行、阻塞、死亡
线程六大状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
- NEW(创建):尚未启动的线程处于此状态。
- RUNNABLE(运行):在Java虚拟机中执行的线程处于该状态。
- BLOCKED(阻塞):等待监视器锁被阻止的线程处于此状态。
- WAITING(等待):无限期等待另一个线程执行特定操作的线程处于此状态。
- TIMED_WAITING(超时等待):在指定的等待时间内等待另一个线程执行操作的线程处于此状态。
- TERMINATED(终止):已退出的线程处于此状态。
package com.demo.thread;
public class ThreadDemo06 {
static class MyThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
MyThread thread = new MyThread();
Thread t = new Thread(thread);
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE
while (Thread.State.TERMINATED != t.getState()) {
Thread.sleep(200);
System.out.println(t.getState()); // TIMED_WAITING
}
// 最后一次 TERMINATED
}
}
四、线程方法
线程停止(stop)
- 不推荐使用JDK提供的stop()、destroy()方法。[@Deprecated]
- 推荐使用标志位变量来终止线程运行。
package com.demo.thread;
public class ThreadDemo02 extends Thread{
// 自定义线程状态变量
private boolean status = true;
@Override
public void run() {
int i = 0;
while (status) {
System.out.println("该线程正在执行" + (i++));
}
}
// 修改线程状态变量
public void stopThread() {
status = false;
}
public static void main(String[] args) {
try {
ThreadDemo02 thread = new ThreadDemo02();
thread.start();
Thread.sleep(1000);
thread.stopThread();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程休眠(sleep)
- Thread.sleep(long millis); 表示当前线程阻塞的毫秒数。
- 使用sleep()方法线程进入阻塞状态,时间到达后线程进入就绪状态。
- 每个对象都有一个锁,sleep()不会释放锁。
package com.demo.thread;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ThreadDemo03 extends Thread{
private boolean status = true;
@Override
public void run() {
int i = 0;
while (status) {
try {
System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()));
Thread.sleep(1000);
if (i++ > 10) {
status = false;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadDemo03 thread = new ThreadDemo03();
thread.start();
}
}
线程礼让(yield)
- yield(); 让当前线程暂停,但不阻塞。
- 让当前线程从运行状态转为就绪状态,重新竞争CPU。
- 让CPU重新调度,礼让不一定成功!
package com.demo.thread;
public class ThreadDemo04 {
static class YieldThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ": 开始启动");
// 线程礼让
Thread.yield();
System.out.println(Thread.currentThread().getName() + ": 结束关机");
}
}
public static void main(String[] args) {
YieldThread yield = new YieldThread();
new Thread(yield, "线程A").start();
new Thread(yield, "线程B").start();
/* A礼让成功,B礼让失败
* 线程A: 开始启动
* 线程B: 开始启动
* 线程B: 结束关机
* 线程A: 结束关机
*/
}
}
线程强制执行(join)
- join(); 待此线程完成后,在执行其他线程。
- 可以理解位现实中的VIP插队。
package com.demo.thread;
public class ThreadDemo05 {
static class JoinThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("join线程正在运行" + (i++));
}
}
}
public static void main(String[] args) {
JoinThread join = new JoinThread();
Thread t = new Thread(join);
t.start();
for (int i = 0; i < 1000; i++) {
if (i == 200) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main线程正在运行" + i);
}
}
}
五、其他
线程优先级(priority)
-
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
-
线程的优先级用数字表示,范围从1~10
- Thread.MIN_PRIORITY = 1;
- Thread.MAX_PRIORITY = 10;
- Thread.NORM_PRIORITY = 5;
-
设置线程优先级方法
- getPriority();
- setPriority(int newPriority);
-
注意:
- 调整线程的优先级只是调整了CPU的调度概率,并不是绝对的。
- 优先级的设置,应该在start()方法执行之前。
守护线程(daemon)
-
Java线程分为用户线程和守护线程
- 用户线程:JVM必须等用户线程全部结束才能结束。eg. main线程
- 守护线程:JVM不用等守护线程结束,也可以结束。eg.GC线程
-
设置守护线程的方法
- Thread.setDaemon(true); // 表示将该线程设置位守护线程
- Thread.setDaemon(false); // 用户线程,默认值位false;
-
注意:
- 设置守护现在要在线程启动之前,要不然会报错IllegalThreadStateException异常
- daemon默认位false,所以线程默认位用户线程