零、概念
在计算机中,我们把一个任务称为一个进程。浏览器是一个进程,word是另一个进程。
一个Java程序实际上是一个JVM进程,JVM用一个主线程来执行
main()
方法,在main()
内部,又可以启动多个线程。另外,还有垃圾回收其他工作线程。当所有非守护线程执行完毕后,JVM进程退出。
线程是操作系统调度的最小任务单位。
进程和线程的关系:包含关系。一个进程可以包含一个或多个线程,但至少一个线程。
进程间通信比线程间通信慢,因为线程间通信是读取同一变量。
一、创建新线程
Java用Thread
对象表示一个线程,线程执行代码写在run()
方法中,一个线程对象只能调用一次start()
方法。
创建新线程:
1 | public class Main { |
必须调用Thread实例的start()
方法才能启动新线程,start()
方法内部调用了一个 private native void start0()
方法。(调用Thread实例的run()
方法相当于调用普通的Java方法,不会启动新线程)。
二、线程的状态
1、六种状态
- New:(初始)新创建的线程,还没有调用
start()
方法; - Runnable:(运行)运行中的线程,正在执行
run()
方法的 Java 代码; - Blocked:(阻塞)运行中的线程,因为某些操作被阻塞而被挂起;
- Waiting:(等待)运行中的线程,进入等待状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断);
- TimeWaiting:(超时等待)运行中的线程,
sleep()
进入计时等待; - Terminated:(终止),线程已终止。
线程终止的原因有:
- 线程正常终止:
run()
方法执行完成; - 线程意外终止:
run()
方法因为未捕获的异常导致线程终止; - 线程强制终止:对某个线程的
Thread
实例调用stop()
方法强制终止(强烈不推荐)
通过对另一个线程对象调用join()
方法可以等待其执行结束。对已经结束的线程调用join()
会立刻返回。
安全地终止线程:thread.interrupt()中断操作或runner.cancel()方法使线程在终止时有机会去清理资源。
三、中断线程
方式一:对目标线程调用 interrupt()
方法
对目标线程调用
interrupt()
方法可以请求中断一个线程,目标线程可以通过检测isInterrupted()
标志获取自身是否已中断。如果目标线程处于等待状态,该线程会捕获到InterruptedException
;目标线程通过检测到
isInterrupted()
为true
或者捕获了InterruptedException
都应该立刻结束自身线程;
方式二 :设置标志位:
目标线程中设置标志位变量public volatile boolean running = true;
,通过标志位判断是否应该继续进行。
外部线程设置标志位值AThread.running = false
,就可以让目标线程中断。
线程间共享变量需要使用volatile
标记(涉及到java内存模型,volatile
解决了共享变量在线程间的可见性问题),确保每个线程读取到更新后的变量值。
中断与终止不同,中断可以选择不退出。
四、守护线程
守护线程是指为其他线程服务的线程。
所有非守护线程执行完毕后,虚拟机退出(不会关心守护线程是否结束)。
在执行start()
启动线程前,线程调用t.setDaemon(true);
可以将线程标记为守护线程。
五、wait和notify
wait
和notify
用于多线程协调运行。
- 必须在
syncronized
块中才能调用wait()
方法,且只能在锁对象上调用wait()
方法 。
wait()
方法是定义在Object
类的一个native
方法。调用wait()
方法时,会释放线程获得的锁,线程处于等待状态。wait()
方法返回后,线程又会试图获得锁。
- 等待的线程如何被唤醒?
wait()
方法什么时候返回?在相同的锁对象上调用notity()
方法。
1 | class TaskQueue { |
六、sleep、yield、wait、join方法区别
1、sleep
static 修饰,Thread 类的静态方法,针对当前线程;
让出CPU,但是不会释放锁。
有超时设置;
可中断方法,可能抛出 InterruptedException。
2、yield
static 修饰,Thread 类的静态方法,针对当前线程;
yield () 方法线程礼让,始终都是Runnable 状态。只能让同优先级的线程有执行的机会,是一种建议,不能保证被采纳。 yield () 只是使当前线程重新回到可执行状态(就绪),所以执行 yield () 的线程有可能在进入到可执行状态后马上又被执行。
3、wait
wait () 和 notify ()、notifyAll () 这三个方法都是 java.lang.Object 的方法。
Object 类的方法 (notify ()、notifyAll () 也是 Object 对象),必须放在循环体和同步代码块中,执行该方法的线程会释放锁,进入线程等待池中等待被再次唤醒 (notify 随机唤醒,notifyAll 全部唤醒,线程结束自动唤醒) 即放入锁池中竞争同步锁。
4、join
join () 是由线程对象来调用。
等待调用 join 方法的线程结束,再继续执行。
当前运行线程调用另一个线程的 join 方法,当前线程进入阻塞状态直到另一个线程运行结束等待该线程终止。 注意该方法也需要捕捉异常。