Java教程

java多线程学习 基础篇(三) Thread类的常用方法

本文主要是介绍java多线程学习 基础篇(三) Thread类的常用方法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

线程Thread是一个程序的多个执行路径,执行调度的单位,依托于进程存在。 线程不仅可以共享进程的内存,而且还拥有一个属于自己的内存空间,这段内存空间也叫做线程栈,是在建立线程时由系统分配的,主要用来保存线程内部所使用的数据,如线程执行函数中所定义的变量。

Java中的多线程是一种抢占机制而不是分时机制。抢占机制指的是有多个线程处于可运行状态,但是只允许一个线程在运行,他们通过竞争的方式抢占CPU。

下面介绍一些常用的Thread方法。

 

Thread.join():静态方法,返回对当前正在执行的线程对象的引用

在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。

Join方法实现是通过wait(小提示:Object 提供的方法)。 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程 ,比如退出后。这就意味着main 线程调用t.join时,必须能够拿到线程t对象的锁。

join() 一共有三个重载版本,分别是无参、一个参数、两个参数:

public final void join() throws InterruptedException; //无参数的join()等价于join(0),作用是一直等待该线程死亡

public final synchronized void join(long millis) throws InterruptedException;  //最多等待该线程死亡millis毫秒
 
public final synchronized void join(long millis, int nanos) throws InterruptedException; //最多等待该线程死亡millis毫秒加nanos纳秒

 

(1) 三个方法都被final修饰,无法被子类重写。

(2) join(long), join(long, long) 是synchronized method,同步的对象是当前线程实例。

(2) 无参版本和两个参数版本最终都调用了一个参数的版本。

(3) join() 和 join(0) 是等价的,表示一直等下去;join(非0)表示等待一段时间。

从源码可以看到 join(0) 调用了Object.wait(0),其中Object.wait(0) 会一直等待,直到被notify/中断才返回。

while(isAlive())是为了防止子线程伪唤醒(spurious wakeup),只要子线程没有TERMINATED的,父线程就需要继续等下去。

(4) join() 和 sleep() 一样,可以被中断(被中断时,会抛出 InterrupptedException 异常);不同的是,join() 内部调用了 wait(),会出让锁,而 sleep() 会一直保持锁。

示例如下:

 1 class TestThread extends Thread {
 2     public TestThread() {
 3         super("[TestThread] Thread");
 4     }
 5 
 6     @Override
 7     public void run() {
 8         String threadName = Thread.currentThread().getName();
 9         System.out.println(threadName + " start.");
10         try {
11             for (int i = 0; i < 5; i++) {
12                 System.out.println(threadName + " loop at " + i);
13                 Thread.sleep(1000);
14             }
15             System.out.println(threadName + " end.");
16         } catch (Exception e) {
17             System.out.println("Exception from " + threadName + ".run");
18         }
19     }
20 }
21 
22 public class JoinDemo {
23     public static void main(String[] args) {
24         String threadName = Thread.currentThread().getName();
25         System.out.println(threadName + " start.");
26         TestThread bt = new TestThread();
27         try {
28             bt.start();
29             bt.join();
30             Thread.sleep(2000);
31         } catch (Exception e) {
32             System.out.println("Exception from main");
33         }
34         System.out.println(threadName + " end!");
35     }
36 }

 

结果如下,主线程会等待子线程执行结束。

>>
main start.
[TestThread] Thread start.
[TestThread] Thread loop at 0
[TestThread] Thread loop at 1
[TestThread] Thread loop at 2
[TestThread] Thread loop at 3
[TestThread] Thread loop at 4
[TestThread] Thread end.
main end!

 

注意:join方法必须在线程start方法调用之后调用才有意义。这个也很容易理解:如果一个线程都没有start,那它也就无法同步了。

 

 

Thread.sleep():暂停线程

 Thread.sleep()被用来暂停当前线程的执行,会通知线程调度器把当前线程在指定的时间周期内置为wait状态。当wait时间结束,线程状态重新变为Runnable并等待CPU的再次调度执行。所以线程sleep的实际时间取决于线程调度器,而这是由操作系统来完成的。

sleep()一共两个重载函数:

java.lang.Thread sleep(long millis)  暂停当前线程的执行,暂停时间由方法参数指定,单位为毫秒。注意参数不能为负数,否则程序将会抛出IllegalArgumentException。

java.lang.Thread sleep(long millis, int nanos)  暂停当前线程的执行,暂停时间为millis毫秒数加上nanos纳秒数。纳秒允许的取值范围为0~999999.

测试用例如下:

 1 public class SleepDemo implements Runnable {
 2     @Override
 3     public void run() {
 4         System.out.println("i am sleep for a while!");
 5         try {
 6             Date currentTime = new Date();
 7             long startTime = currentTime.getTime();
 8             Thread.sleep(4000);
 9             currentTime = new Date();
10             long endTime = currentTime.getTime();
11             System.out.println("休眠时间为:" + (endTime - startTime) + "ms");
12         } catch (InterruptedException e) {
13             e.printStackTrace();
14         }
15     }
16 
17     public static void main(String[] args) {
18         SleepDemo runnable = new SleepDemo();
19         Thread thread = new Thread(runnable);
20         thread.start();
21     }
22 }

结果为:

>>>
i am sleep for a while!
休眠时间为:4002ms

总结:

1. 它只用于暂停当前线程的执行。

2. 线程被唤醒(wake up)并开始执行的实际时间取决于操作系统的CPU时间片长度及调度策略。对于相对空闲的系统来说,sleep的实际时间与指定的sleep时间相近,但对于操作繁忙的系统,这个时间将会显得略长一些。

3. 线程在sleep过程中不会释放它已经获得的任意的monitor和lock等资源。

4. 其他的任意线程都能中断当前sleep的线程,并会抛出InterruptedException。

 

Thread.interrupt() 中断线程

interrupt()的作用是中断本线程。

1. 本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。

2. 如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。

3. 如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。

4. 如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。

5. 中断一个“已终止的线程”不会产生任何操作。

例如:

 1 class InterruptDemo extends Thread {
 2     @Override
 3     public void run() {
 4         while (!Thread.currentThread().isInterrupted()) {
 5             System.out.println("Thread running...");
 6             try {
 7                 /*
 8                  * 如果线程阻塞,将不会去检查中断信号量stop变量,所以thread.interrupt()
 9                  * 会使阻塞线程从阻塞的地方抛出异常,让阻塞线程从阻塞状态逃离出来,并
10                  * 进行异常块进行 相应的处理
11                  */
12                 // 线程阻塞,如果线程收到中断操作信号将抛出异常
13                 Thread.sleep(1000);
14             } catch (InterruptedException e) {
15                 System.out.println("Thread interrupted...");
16                 /*
17                  * 如果线程在调用 Object.wait()方法,或者该类的 join() 、sleep()方法
18                  * 过程中受阻,则其中断状态将被清除
19                  */
20                 System.out.println(this.isInterrupted());
21                 // false
22 
23                 //中不中断由自己决定,如果需要真正中断线程,则需要重新设置中断位,如果
24                 //不需要,则不用调用
25                 Thread.currentThread().interrupt();
26             }
27         }
28         System.out.println("Thread exiting under request...");
29     }
30 
31     public static void main(String args[]) throws Exception {
32         InterruptDemo thread = new InterruptDemo();
33         System.out.println("Starting thread...");
34         thread.start();
35         Thread.sleep(3000);
36         System.out.println("Asking thread to stop...");
37         thread.interrupt();// 等中断信号量设置后再调用
38         Thread.sleep(3000);
39         System.out.println("Stopping application...");
40     }
41 }

总结

1. Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,设置线程的中断标示位,在线程受到阻塞的地方(如调用sleep、wait、join等地方)抛出一个异常InterruptedException,并且中断状态也将被清除,这样线程就得以退出阻塞的状态。
2. 一般来说,阻塞函数,如:Thread.sleep、Thread.join、Object.wait等在检查到线程的中断状态时,会抛出InterruptedException,同时会清除线程的中断状态

 

Thread synchronized同步锁

在java中,每一个对象有且仅有一个同步锁。这也意味着,同步锁是依赖于对象而存在。当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。例如synchronized(obj)就获取了“obj这个对象”的同步锁。

原理

当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。不同线程对同步锁的访问是互斥的,也就是说,某时间点,对象的同步锁只能被一个线程获取到!通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。 例如,现在有两个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁” —— 线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。

用法

1. 修饰普通方法,对普通方法同步

修饰一个普通方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。

 1 class SynchronizedTest1 {
 2     public synchronized void method1(){
 3         System.out.println("Method 1 start");
 4         try {
 5             System.out.println("Method 1 execute");
 6             Thread.sleep(3000);
 7         } catch (InterruptedException e) {
 8             e.printStackTrace();
 9         }
10         System.out.println("Method 1 end");
11     }
12 
13     public synchronized void method2(){
14         System.out.println("Method 2 start");
15         try {
16             System.out.println("Method 2 execute");
17             Thread.sleep(1000);
18         } catch (InterruptedException e) {
19             e.printStackTrace();
20         }
21         System.out.println("Method 2 end");
22     }
23 }
24 
25 public class SynchronizedDemo1{
26     static void testDemo1(){
27         SynchronizedTest1 test = new SynchronizedTest1();
28         new Thread(new Runnable() {
29             @Override
30             public void run() {
31                 test.method1();
32             }
33         }).start();
34 
35         new Thread(() -> test.method2()).start();
36     }
37 
38     public static void main(String[] args) {
39         testDemo1();
40     }
41 }

线程2需要等待线程1的method1执行完成才能开始执行method2方法。执行结果如下:

>>>
Method 1 start
Method 1 execute
Method 1 end
Method 2 start
Method 2 execute
Method 2 end

 

2. 修饰静态方法,静态方法(类)同步

修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象。

 1 class SynchronizedTest2 {
 2     public static synchronized void method1(){
 3         System.out.println("Method 1 start");
 4         try {
 5             System.out.println("Method 1 execute");
 6             Thread.sleep(3000);
 7         } catch (InterruptedException e) {
 8             e.printStackTrace();
 9         }
10         System.out.println("Method 1 end");
11     }
12 
13     public static synchronized void method2(){
14         System.out.println("Method 2 start");
15         try {
16             System.out.println("Method 2 execute");
17             Thread.sleep(1000);
18         } catch (InterruptedException e) {
19             e.printStackTrace();
20         }
21         System.out.println("Method 2 end");
22     }
23 }
24 
25 public class SynchronizedDemo1 {
26     public static void main(String[] args) {
27         testDemo2();
28     }
29 
30     static void testDemo2() {
31         final SynchronizedTest2 test = new SynchronizedTest2();
32         final SynchronizedTest2 test2 = new SynchronizedTest2();
33         new Thread(new Runnable() {
34             @Override
35             public void run() {
36                 //不应该通过类实例访问静态成员 com.thread.SynchronizedTest2.method1() 应该通过类名直接访问,这里只是为了演示
37                 test.method1();
38             }
39         }).start();
40 
41         new Thread(new Runnable() {
42             @Override
43             public void run() {
44                 test2.method2();
45             }
46         }).start();
47     }
48 }

对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法),所以即使test和test2属于不同的对象,但是它们都属于SynchronizedTest类的实例,所以也只能顺序的执行method1和method2,不能并发执行。执行结果如下:

>>>
Method 1 start
Method 1 execute
Method 1 end
Method 2 start
Method 2 execute
Method 2 end

3. 修饰代码块,代码块同步

修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象。

 1 class SynchronizedTest3 {
 2     public void method1(){
 3         System.out.println("Method 1 start");
 4         try {
 5             synchronized (this) {
 6                 System.out.println("Method 1 execute");
 7                 Thread.sleep(3000);
 8             }
 9         } catch (InterruptedException e) {
10             e.printStackTrace();
11         }
12         System.out.println("Method 1 end");
13     }
14 
15     public void method2(){
16         System.out.println("Method 2 start");
17         try {
18             synchronized (this) {
19                 System.out.println("Method 2 execute");
20                 Thread.sleep(1000);
21             }
22         } catch (InterruptedException e) {
23             e.printStackTrace();
24         }
25         System.out.println("Method 2 end");
26     }
27 
28 }
29 
30 public class SynchronizedDemo1 {
31     public static void main(String[] args) {
32         testDemo3();
33     }
34 
35     static void testDemo3() {
36         final SynchronizedTest3 test = new SynchronizedTest3();
37 
38         new Thread(new Runnable() {
39             @Override
40             public void run() {
41                 test.method1();
42             }
43         }).start();
44 
45         new Thread(new Runnable() {
46             @Override
47             public void run() {
48                 test.method2();
49             }
50         }).start();
51     }
52 
53 }

虽然线程1和线程2都进入了对应的方法开始执行,但是线程2在进入同步块之前,需要等待线程1中同步块执行完成。执行结果如下:

>>>
Method 1 start
Method 1 execute
Method 2 start
Method 1 end
Method 2 execute
Method 2 end

 

4. 修饰一个类,对类同步

修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

 1 class SyncThread implements Runnable {
 2     private static int count;
 3 
 4     public SyncThread() {
 5         count = 0;
 6     }
 7 
 8     public static void method() {
 9         synchronized(SyncThread.class) {
10             for (int i = 0; i < 5; i ++) {
11                 try {
12                     System.out.println(Thread.currentThread().getName() + ":" + (count++));
13                     Thread.sleep(100);
14                 } catch (InterruptedException e) {
15                     e.printStackTrace();
16                 }
17             }
18         }
19     }
20 
21     @Override
22     public synchronized void run() {
23         method();
24     }
25 }
26 
27 public class SynchronizedDemo1 {
28     public static void main(String[] args) {
29         testSyncThread();
30     }
31 
32     static void testSyncThread() {
33         SyncThread t = new SyncThread();
34         new Thread(t).start();
35         new Thread(t).start();
36     }
37 
38 }

synchronized作用于一个类T时,是给这个类T加锁,T的所有对象用的是同一把锁。顺序执行:

>>>
Thread-0:0
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-1:5
Thread-1:6
Thread-1:7
Thread-1:8
Thread-1:9

基本规则

1. 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

2. 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。

3. 当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。

总结

1、当多个线程同时执行synchronized(x){}同步代码块时呈同步效果。当其他线程执行x对象中的synchronized同步方法时呈同步效果。当其他线程执行x对象方法中的synchronized(this)代码块时也呈同步效果。

2. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。 

3. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。 

4. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

 

Thread.yield() 线程协作让步

Thread yield()方法的作用是暂停当前线程,以便其他线程有机会执行,不过不能指定暂停的时间,并且也不能保证当前线程马上停止。yield方法只是将Running状态转变为Runnable状态。

 1 class ThreadA extends Thread{
 2     public ThreadA(String name){
 3         super(name);
 4     }
 5     @Override
 6     public synchronized void run(){
 7         for(int i=0; i <10; i++){
 8             System.out.printf("%s [%d]:%d\n", this.getName(), this.getPriority(), i);
 9             // i整除4时,调用yield
10             if (i%4 == 0) {
11                 Thread.yield();
12             }
13         }
14     }
15 }
16 
17 public class YieldDemo{
18     public static void main(String[] args){
19         ThreadA t1 = new ThreadA("t1");
20         ThreadA t2 = new ThreadA("t2");
21         t1.start();
22         t2.start();
23     }
24 }

执行结果:

>>>
t1 [5]:0
t2 [5]:0
t2 [5]:1
t2 [5]:2
t2 [5]:3
t2 [5]:4
t2 [5]:5
t2 [5]:6
t2 [5]:7
t2 [5]:8
t2 [5]:9
t1 [5]:1
t1 [5]:2
t1 [5]:3
t1 [5]:4
t1 [5]:5
t1 [5]:6
t1 [5]:7
t1 [5]:8
t1 [5]:9

“线程t1”在能被4整数的时候,并没有切换到“线程t2”。这表明,yield()虽然可以让线程由“运行状态”进入到“就绪状态”;但是,它不一定会让其它线程获取CPU执行权(即,其它线程进入到“运行状态”),即使这个“其它线程”与当前调用yield()的线程具有相同的优先级。

总结

1. 使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。

2. 用了yield方法后,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)

3. 通过yield方法来实现两个线程的交替执行。不过请注意:这种交替并不一定能得到保证。

4. yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会

 

Thread sleep(),wait()区别详解

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。sleep用于线程控制,而wait用于线程间的通信。

sleep()简介

1. sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;

2. sleep()是Thread类的Static(静态)的方法;因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。

3. 在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。 

wait()简介

1. wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁);其他线程可以访问;

2. wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前等待池中的线程。

3. wiat()必须放在synchronized block中,否则会在program runtime时扔出”java.lang.IllegalMonitorStateException“异常。

区别

区别一

1. sleep是Thread类的方法,是线程用来 控制自身流程的,比如有一个要报时的线程,每一秒中打印出一个时间,那么我就需要在print方法前面加上一个sleep让自己每隔一秒执行一次。就像个闹钟一样。
2. wait是Object类的方法,用来线程间的通信,这个方法会使当前拥有该对象锁的进程等待知道其他线程调用notify方法时再醒来,不过你也可以给他指定一个时间,自动醒来。这个方法主要是用走不同线程之间的调度的。

区别二

1. 关于锁的释放 ,在这里假设大家已经知道了锁的概念及其意义。调用sleep方法不会释放锁(自己的感觉是sleep方法本来就是和锁没有关系的,因为他是一个线程用于管理自己的方法,不涉及线程通信)

2. 调用wait方法会释放当前线程的锁(其实线程间的通信是靠对象来管理的,所有操作一个对象的线程是这个对象通过自己的wait方法来管理的,就好像这个对象是电视机,三个人是三个线程,那么电视机的遥控器就是这个锁,假如现在A拿着遥控器,电视机调用wait方法,那么A就交出自己的遥控器,由jVM虚拟机调度,遥控器该交给谁。)

区别三

由于wait函数的特殊意义,所以他是应该放在同步语句块中的,这样才有意义。

 1 public class TestWaitAndSleep {
 2     public static void main(String[] args) {
 3         new Thread(new Thread1()).start();
 4         try {
 5             Thread.sleep(5000);
 6         } catch (Exception e) {
 7             e.printStackTrace();
 8         }
 9         new Thread(new Thread2()).start();
10     }
11 
12     private static class Thread1 implements Runnable{
13         @Override
14         public void run(){
15             synchronized (TestWaitAndSleep.class) {
16                 System.out.println("enter thread1...");
17                 System.out.println("thread1 is waiting...");
18                 try {
19                     //调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池
20                     TestWaitAndSleep.class.wait();
21                 } catch (Exception e) {
22                     e.printStackTrace();
23                 }
24                 System.out.println("thread1 is going on ....");
25                 System.out.println("thread1 is over!!!");
26             }
27         }
28     }
29 
30     private static class Thread2 implements Runnable{
31         @Override
32         public void run(){
33             synchronized (TestWaitAndSleep.class) {
34                 System.out.println("enter thread2....");
35                 System.out.println("thread2 is sleep....");
36                 //只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
37                 TestWaitAndSleep.class.notify();
38                 //==================
39                 //区别
40                 //如果我们把代码:TestD.class.notify();给注释掉,即TestD.class调用了wait()方法,但是没有调用notify()
41                 //方法,则线程永远处于挂起状态。
42                 try {
43                     //sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,
44                     //但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
45                     //在调用sleep()方法的过程中,线程不会释放对象锁。
46                     Thread.sleep(5000);
47                 } catch (Exception e) {
48                     e.printStackTrace();
49                 }
50                 System.out.println("thread2 is going on....");
51                 System.out.println("thread2 is over!!!");
52             }
53         }
54     }
55 }

运行结果:

>>>
enter thread1...
thread1 is waiting...
enter thread2....
thread2 is sleep....
thread2 is going on....
thread2 is over!!!
thread1 is going on ....
thread1 is over!!!

 

如果注释掉代码:TestD.class.notify();

运行结果:

>>>
enter thread1...
thread1 is waiting...
enter thread2....
thread2 is sleep....
thread2 is going on....
thread2 is over!!!

且程序一直处于挂起状态。

总结

1. sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。在调用sleep()方法的过程中,线程不会释放对象锁。

2. 调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

 

这篇关于java多线程学习 基础篇(三) Thread类的常用方法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!