线程基础
进程与线程的关系
进程
- 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程本质上来说就是用来加载指令、管理内存、管理IO的。
- 当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。
- 进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐、360 安全卫士等)。
线程
- 一个进程之内可以分为一到多个线程。
- 一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给 CPU执行。
- 在Java中,线程作为最小调度单位,进程作为资源分配的最小单位。在windows中进程是不活动的,只是作为线程的容器。
二者对比
- 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集。
- 进程拥有共享的资源,如内存空间等,供其内部的线程共享。
- 进程间通信较为复杂:
-
- 同一台计算机的进程通信称为IPC(Inter-process communication)。
-
- 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP。
- 线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量。
- 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低。
线程上下文切换
由于以下一些原因导致cpu不再执行当前的线程,转而执行另一个线程的代码。
- 线程的CPU时间片用完。
- 垃圾回收。
- 有更高优先级的线程需要运行。
- 线程自己调用
slepp
,yield
,wait
,join
,park
,synchronized
,lock
等方法。 - 当
Context Switch
发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java中对应的概念就是程序计数器,它的作用是记住下一条jvm指令的执行地址,是线程私有的。
并发与并行的关系
并发
- 单核CPU下,线程实际还是串行执行的。操作系统中有一个组件叫做任务调度器,将CPU的时间片(windows下时间片最小约15毫秒) 分给不同线程使用,只是由于CPU在线程间(时间片很短)的切换非常快。人类感觉是同时运行的。总结一句话就是:
微观串行,宏观并行
。 - 一般会将这种线程轮流使用CPU的做法称为并发(concurrent)。
并行
多核CPU下,每个核(core) 都可以调度运行线程,这时候线程可以是并行的。
并发 + 并行
- 执行任务的数量恰好等于CPU核心的数量,是一种理想状态。但是在实际场景中,处于运行状态的任务是非常多的,尤其是电脑和手机,开机就几十个任务,而 CPU 往往只有 4 核、8 核或者 16 核,远低于任务的数量,这个时候就会同时存在并发和并行两种情况:所有核心都要并行工作,并且每个核心还要并发工作。
- 例如一个双核 CPU 要执行四个任务,它的工作状态如下图所示:
-每个核心并发执行两个任务,两个核心并行的话就能执行四个任务。当然也可以一个核心执行一个任务,另一个核心并发执行三个任务,这跟操作系统的分配方式,以及每个任务的工作状态有关系。
总结
- 并发针对单核 CPU 而言,它指的是 CPU 交替执行不同任务的能力;并行针对多核 CPU 而言,它指的是多个核心同时执行多个任务的能力。
- 单核 CPU 只能并发,无法并行;换句话说,并行只可能发生在多核 CPU 中。
- 在多核 CPU 中,并发和并行一般都会同时存在,它们都是提高 CPU 处理任务能力的重要手段。
创建运行线程
继承Thread类
//创建线程
Thread thread = new Thread(() -> logger.debug("running..."));
//启动线程
thread.start();
实现Runable接口
//创建任务
Runnable runnable = () -> {
logger.debug("running...");
}
//任务交给线程
Thread thread = new Thread(runnable);
//启动线程
thread.start();
实现Callable接口
//创建任务
FutureTask<Object> futureTask = new FutureTask<Object>(() -> {
logger.debug("running...");
//线程结束后将执行结果返回
return 100;
});
//创建线程
Thread thread = new Thread(futureTask);
//启动线程
thread.start();
//线程结束后的执行结果
System.out.println(futureTask.get());
线程状态
操作系统
从操作系统方面来说,线程有5种状态:
- 初始状态:仅在语言层面创建了线程对象,还未与操作系统关联。
- 可运行状态:指的是线程已被创建(与操作系统相关联),可以有CPU调度并执行。
- 运行状态:指获取了CPU时间片运行中的状态。但是当CPU时间片用完后,会从运行状态转换至可运行状态,会导致线程上下文的切换。
- 阻塞状态:如果调用了阻塞API,如BIO读写文件,这时候线程实际上不会用到CPU,会导致线程上下文切换,进入阻塞状态。等BIO操作完毕,会由操作系统唤醒阻塞的线程,切换至可运行状态。与可运行状态的区别是,对于阻塞状态的线程来说只要他们一直不被唤醒,调度器就一直不会考虑调度它们。
- 终止状态:表示线程已经执行结束,不会再转换为其它状态。
Java:
从java方面来说,线程有6种状态:
public enum State {
/**
* 线程刚被创建,但未调用start方法。
*/
NEW,
/**
* runnable当调用start()方法之后,JavaApi层面的Runnable状态涵盖了操作系统层面的可运行状态,运行 * 状态以及阻塞状态,由于BIO导致的线程阻塞,在java里面无法区分,所以仍然认为是可运行的。
*/
RUNNABLE,
/**
* 阻塞状态,表示线程阻塞于锁
*/
BLOCKED,
/**
* WAITING会进入一个无时间限制的等,一般来说,WAITING的线程正式在等待一些特殊的事件,比如,通过 * wait()方法等待的线程在等待notify()方法,而通过join()方法等待的线程则会等待目标线程的终止。一 * 旦等到期望的事件,线程就会再次进入RUNNABLE运行状态。
*/
WAITING,
/**
* 超时等待状态,可以指定等待时间自己返回
*/
TIMED_WAITING,
/**
* 终止状态,表示当前线程已经执行完毕
*/
TERMINATED;
}
NEW 状态 代码示例
public class ThreadStateNewExample {
private static Object waiter = new Object();
public static void main(String[] args){
Runnable waiting = () -> {
try{
waiter.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
};
Thread whoWillWait = new Thread(waiting);
System.out.printf(whoWillWait.getState().toString());
}
}
//运行结果
NEW
Process finished with exit code 0
RUNNABLE 状态 代码示例
public class ThreadStateRunnableExample {
private static boolean flag = true;
public static void main(String[] args){
Runnable waiting = () -> {
//让程序空转,保持线程是runnable状态
do { }while (flag);
};
Thread thread = new Thread(waiting);
thread.start();
try {
//主线程先睡3秒,让子线程先跑起来,然后输出线程状态
Thread.sleep(3000);
System.out.printf(thread.getState().toString());
//更改标志位,让子线程结束循环
flag = false;
}catch (InterruptedException e){
e.printStackTrace();
}
System.exit(1);
}
}
//运行结果
RUNNABLE
Process finished with exit code 1
BLOCKED 状态 代码示例
public class ThreadStateBlockExample {
private static boolean LOCK_FLAG = true;
public static void main(String[] args){
Runnable locker = ThreadStateBlockExample::locker;
Thread whoWillLockOthers = new Thread(locker);
/**
* 启动whoWillLockOthers线程,主线程睡2秒让子线程先运行
* 此时whoWillLockOthers获得锁,这时候其他线程需要等待
*/
whoWillLockOthers.start();
try {
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
Thread whoWillBeLocked = new Thread(locker);
/**
* 启动whoWillBeLocked线程,主线程睡2秒让子线程先运行
* 因为locker方法是个死循环,所以whoWillBeLocked线程永远拿不到锁,就会进入BLOCKED状态
*/
whoWillBeLocked.start();
try {
Thread.sleep(2000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.printf("whoWillBeLocked 当前状态="+whoWillBeLocked.getState().toString()+"");
System.exit(1);
}
private static synchronized void locker(){
do {
}while (LOCK_FLAG);
}
}
//执行结果
whoWillBeLocked 当前状态为=BLOCKED
Process finished with exit code -1
WAITING 状态 代码示例
//代表线程正在等待中。一个线程如果调用下列方法,会导致线程状态变为`WAITING`:
//- `Object.wait with no timeout`
//- `Thread.join with no timeout`
//- `LockSupport.park`
//举个栗子:`ThreadA`调用了`Object.wait()`方法,此时`ThreadA`状态为`WAITING`。`ThreadA`会等待其他//的线程调用 `Object.notify()`或`Object.notifyAll`才会被唤醒,继续执行后面的逻辑。
//注意!!!调用`wait()`和`notify()`此类方法必须先获得`Object`的锁。
public class ThreadStateWaitingExample {
private static final Object LOCKER = new Object();
public static void main(String[] args) {
Runnable waiting = () -> {
System.out.println("whoWillWait 开始等待 whoWillNotify");
waiting();
System.out.println("whoWillWait 等到了 whoWillNotify 的通知");
};
//创建一个线程调用waiter.wait()方法,让whoWillWait线程进入waiting状态
Thread whoWillWait = new Thread(waiting);
whoWillWait.start();
//主线程先睡2秒,让whoWillWait先执行
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("whoWillWait当前的线程状态=" + whoWillWait.getState().toString());
Runnable notify = () -> {
System.out.println("whoWillNotify 准备通知 whoWillWait");
notifying();
};
//创建一个线程调用waiter.notify()方法,唤醒whoWillWait
Thread whoWillNotify = new Thread(notify);
whoWillNotify.start();
//主线程先睡2秒,让whoWillNotify先执行
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("唤醒后,whoWillWait当前的线程状态=" + whoWillWait.getState().toString());
System.exit(1);
}
private static void waiting() {
synchronized (LOCKER) {
try {
LOCKER.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static void notifying() {
synchronized (LOCKER) {
LOCKER.notify();
System.out.println("whoWillNotify 已经通知,即将离开同步代码块");
}
}
}
//执行结果
whoWillWait 开始等待 whoWillNotify
whoWillWait当前的线程状态=WAITING
whoWillNotify 准备通知 whoWillWait
whoWillNotify 已经通知,即将离开同步代码块
whoWillWait 等到了 whoWillNotify 的通知
唤醒后,whoWillWait当前的线程状态=TERMINATED
Process finished with exit code 1
TIMED_WAITING 状态 代码示例
//线程正在等待其他线程的操作,直到超过指定的超时时间。线程在调用以下方法是会将状态改变为TIMED_WAITING状态:
//- Thread.sleep
//- Object.wait with timeout
//- Thread.join with timeout
//- LockSupport.parkNanos
//- LockSupport.parkUntil
public class ThreadStateTimedWaitingExample {
private static final Object LOCKER = new Object();
public static void main(String[] args) {
Runnable waiting = () -> {
System.out.println("whoWillWait 开始等待 2秒钟");
waiting();
System.out.println("whoWillWait 等待结束");
};
//创建一个线程调用waiter.wait()方法,让whoWillWait线程进入waiting状态
Thread whoWillWait = new Thread(waiting);
whoWillWait.start();
//主线程先睡1秒,让whoWillWait先执行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("whoWillWait当前的线程状态=" + whoWillWait.getState().toString());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("whoWillWait当前的线程状态=" + whoWillWait.getState().toString());
System.exit(1);
}
private static void waiting() {
synchronized (LOCKER) {
try {
LOCKER.wait(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//执行结果
whoWillWait 开始等待 2秒钟
whoWillWait当前的线程状态=TIMED_WAITING
whoWillWait 等待结束
whoWillWait当前的线程状态=TERMINATED
Process finished with exit code 1
TERMINATED 状态 代码示例
public class ThreadStateTerminatedExample {
public static void main(String[] args) {
Runnable waiting = () -> {
System.out.println("随便执行一下,然后线程就会变为Terminated");
};
Thread terminate = new Thread(waiting);
terminate.start();
//主线程先睡1秒,让terminate先执行,一秒钟足够terminate执行完毕,然后线程就结束了
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("terminate当前的线程状态=" + terminate.getState().toString());
System.exit(1);
}
}
//执行结果
随便执行一下,然后线程就会变为Terminated
terminate当前的线程状态=TERMINATED
Process finished with exit code 1
线程在某个时间点只会拥有一种状态,这些状态都是虚拟机的线程状态,并不是操作系统的线程状态,这是两个概念,不要混淆。
常用方法
currentThread方法
Thread.currentThread()方法可以返回的是当前正在被执行的线程
run方法
run方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行> >路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达> >到写线程的目的。
案例:
@Slf4j
public class Test {
public static void main(String[] args) {
Thread thread = new Thread("线程一"){
@Override
public void run() {
log.info("running...");
}
};
log.info("main线程 running...");
thread.run();
}
}
//执行结果:
//18:04:08.933 [main] INFO com.dqxh.controller.Test - main线程 running...
//18:04:08.935 [main] INFO com.dqxh.controller.Test - running...
start方法
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run方法,这里方法run称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。
getState方法
获取指定线程的当前状态。
案例:
@Slf4j
public class Test {
public static void main(String[] args) {
Thread thread = new Thread("线程一"){
@Override
public void run() {
log.info("running...");
}
};
System.out.println(thread.getState());
thread.start();
System.out.println(thread.getState());
}
}
//执行结果:
//18:15:02.820 [main] INFO com.dqxh.controller.Test - main线程 running...
//NEW
//RUNNABLE
//18:15:02.822 [线程一] INFO com.dqxh.controller.Test - running...
sleep方法
让当前线程休眠,使其放弃CPU时间片的使用,调用sleep方法会让当前线程从Running进入Timed Waiting(阻塞)状态,其它线程可以使用interrupt方法打断正在休眠的线程,这时sleep方法会抛出
InterruptedException
,睡眠之后的线程未必会立刻执行,建议使用TimeUnit的sleep代替Thread的sleep来获得更好的可读性。
案例:
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread("线程一") {
@Override
public void run() {
try {
//Thread.sleep(2000);
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
log.info(e.getMessage());
}
}
};
thread.start();
//让主线程休眠
//Thread.sleep(1000);
TimeUnit.SECONDS.sleep(2);
System.out.println(thread.getName() +"状态:"+ thread.getState());
}
}
//执行结果:
//线程一状态:TIMED_WAITING
yield方法
调用yield会让当前线程从Running进入Runnable(就绪)状态,然后调度执行其它线程,具体实现比较依赖操作系统的任务调度器。线程执行sleep方法后会转入阻塞状态,所以执行sleep()方法的线程在指定的时间内肯定不会被执行,而yield方法只是使当前线程重新回到就绪状态,所以执行yield()方法的线程有可能在进入到可执行状态后马上又被执行。
案例:
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Runnable task1 = ()->{
int count = 0;
for (;;){
log.info("线程一" + count++);
}
};
Runnable task2 = ()->{
int count = 0;
for (;;){
Thread.yield();
log.info("-------------->" + count++);
}
};
Thread thread1 = new Thread(task1 ,"线程一");
Thread thread2 = new Thread(task2 , "线程二");
thread1.start();
thread2.start();
}
}
//执行结果:
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381879
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381880
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381881
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381882
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381883
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381884
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381885
//20:29:13.660 [线程二] INFO com.dqxh.controller.Test - =========>222094
//20:29:13.660 [线程二] INFO com.dqxh.controller.Test - =========>222095
//20:29:13.660 [线程二] INFO com.dqxh.controller.Test - =========>222096
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381886
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381887
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381888
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381889
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381890
setPriority方法
调用setPriority方法,根据参数的不同可以设置不同的线程优先级,但是它仅仅只是提示哪一个线程该优先执行,调度器是完全可以忽略它。如果CPU比较忙,那么优先级高的线程会获得更多的时间片,但是CPU空闲时,优先级几乎没用。
案例:
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Runnable task1 = ()->{
int count = 0;
for (;;){
log.info("--->"+count++);
}
};
Runnable task2 = ()->{
int count = 0;
for (;;){
log.info("=========>" + count++);
}
};
Thread thread1 = new Thread(task1 ,"线程一");
Thread thread2 = new Thread(task2 , "线程二");
//线程一设置最小优先级
thread1.setPriority(Thread.MIN_PRIORITY);
//线程二设置最大优先级
thread2.setPriority(Thread.MAX_PRIORITY);
thread1.start();
thread2.start();
}
}
join方法
join方法就是通过wait方法实现的,让主线程处于WAITING(等待)状态,主要作用就是同步,它可以使得线程之间的并行执行变为串行执行。
案例:
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
log.info("线程一执行完毕!");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, "线程一");
thread1.start();
//程序在main线程中调用thread1线程的join方法,则main线程放弃cpu控制权
//并返回t1线程继续执行直到线程t1执行完毕后main线程才有执行的机会。
thread1.join();
log.info("main线程执行完毕!");
}
}
//执行结果:
//20:54:50.110 [线程一] INFO com.dqxh.controller.Test - 线程一执行完毕!
//20:54:50.112 [main] INFO com.dqxh.controller.Test - main线程执行完毕!
interrupt方法
在一个线程中调用另一个线程的interrupt()方法,即会设置那个线程的中断状态。该方法不能中断在运行中的线程,它只能改变中断状态而已。但是该方法打断sleep,wait,join状态下的线程后,会以异常
InterruptedException
的形式标识,线程的中断标志位会由true重置为false,因为线程为了处理异常已经重新处于就绪状态。isInterrupted方法
该方法用于标记线程是否被打断。
interrupted方法
该方法用于标记线程是否被打断,并且会清除打断标记
案例:
//打断正常情况下的线程
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
log.info("thread1线程已经被打断");
break;
}
log.info("thread1线程正在运行中");
}
});
thread1.start();
//让main线程睡眠1s是因为让thread1线程进入休眠状态后,再进行打断,此时打断标记变为false。
TimeUnit.SECONDS.sleep(1);
thread1.interrupt();
log.info("打断标记:{}", thread1.isInterrupted());
}
}
//执行结果:
//23:25:41.244 [Thread-0] INFO com.wuke.controller.Test - thread1线程正在运行中
//23:25:41.244 [Thread-0] INFO com.wuke.controller.Test - thread1线程正在运行中
//23:25:41.244 [Thread-0] INFO com.wuke.controller.Test - thread1线程正在运行中
//23:25:41.244 [Thread-0] INFO com.wuke.controller.Test - thread1线程已经被打断
//23:25:41.244 [main] INFO com.wuke.controller.Test - 打断标记:true
//打断sleep/wait/join状态下的线程,其该线程状态依然为false
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
thread1.start();
//让main线程睡眠1s是因为让thread1线程进入休眠状态后,再进行打断,此时打断标记变为false。
TimeUnit.SECONDS.sleep(1);
//main线程打断thread1线程
thread1.interrupt();
log.info("打断标记:{}" ,thread1.isInterrupted());
}
}
//执行结果:
//Caused by: java.lang.InterruptedException: sleep interrupted
// at java.lang.Thread.sleep(Native Method)
// at java.lang.Thread.sleep(Thread.java:340)
// at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
// at com.wuke.controller.Test.lambda$main$0(Test.java:13)
// ... 1 more
//23:07:14.650 [main] INFO com.wuke.controller.Test - 打断标记:false
setDaemon
默认情况下Java进程需要等待所有线程运行结束,Java进程才会结束。但是有一种特殊的线程只要其它非守护线程运行结束了,它才会结束,即使守护线程的代码并没有执行完毕,也会被强制结束。 垃圾回收器就是一个守护线程 Tomcat中的Acceptor和Poller线程都是守护线程,所以Tomcat再收到shutdown命令后,不会等待它们处理完当前请求。
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(()->{
while (true){
log.info("守护线程执行中");
}
},"守护线程");
//将thread1设置为守护线程
thread1.setDaemon(true);
thread1.start();
log.info("main线程结束");
}
}
//10:15:40.490 [main] INFO com.dqxh.controller.Test - main线程结束
//10:15:40.490 [守护线程] INFO com.dqxh.controller.Test - 守护线程执行中
//10:15:40.492 [守护线程] INFO com.dqxh.controller.Test - 守护线程执行中
//10:15:40.492 [守护线程] INFO com.dqxh.controller.Test - 守护线程执行中
过时方法 stop(),suspend(),resume()
stop():停止线程运行。
suspend():挂起(暂停线程)。
resume():恢复线程运行。不推荐使用,这些方法已经过时,容易破坏同步代码块,造成线程死锁。
总结:
yield() 和 sleep() 的异同
- 相同点:
yield()
方法和sleep()
方法类似,也不会释放“锁”。 - 不同点:
yield()
方法只是使当前线程重新回到可执行状态,所以执行yield()
的线程有可能在进入到可执行状态后马上又被执行。yield()
方法只能使同优先级或者高优先级的线程得到执行机会,这也和sleep()
方法不同。
join() 和 sleep() 的异同
- 相同点:都可以实现等待
- 不同点:由于
join
的内部实现是wait()
,所以使用join()
方法时会释放锁,那么其他线程就可以调用此线程的同步方法了。sleep()
方法不释放锁,因此线程会一直等待下去,直到任务完成,才会释放锁。
sleep() 与 wait() 的异同
- 相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
- 不同点:
- 两个方法声明的位置不同:
Thread
类中声明sleep()
,Object
类中声明wait()
。 - 调用的要求不同:
sleep()
可以在任何需要的场景下调用。wait()
必须使用在同步代码块或同步方法中 - 关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,
sleep()
不会释放锁,wait()
会释放锁。 - 当调用某一对象的
wait()
方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了notify()
方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的notifyAll()
方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。
- 两个方法声明的位置不同: