多线程(六)Thread和Object类中和线程相关的重要方法

2 篇文章 0 订阅
2 篇文章 0 订阅

大家好啊,我是小张,今天介绍一下Thread和Object类中和线程相关的重要方法这一章节的第一部分-----wait()、notify()、notifyAll()方法

``

1.wait()/notify()/notifyAll()方法详解

1.1. 方法概览

方法名简介
Threadsleep相关"相关"指的是重载,例如sleep有多个方法,参数不同,实际作用大同小异
Threadjoin等待其他线程执行完毕
Threadyield相关放弃已经获取到的CPU资源
ThreadcurrentThread获取当前线程的引用
Threadstart/run相关启动线程相关
Threadinterrupt相关中断线程
Threadstop()/suspend()/resuem() 相关已废弃
Objectwait()/notify()/notifyAll() 相关让线程暂时休息和唤醒

1.2.wait()/notify()/notifyAll()作用

1.2.1 阻塞阶段:

如果某个线程调用wait方法,那么这个线程会释放掉获取的锁并进入Waiting状态,注意wait方法只能在synchronized代码块内调用,直到以下4种情况才会被唤醒:

  • 另外一个线程调用这个对象的notify()方法且刚好被唤醒的是本线程;
  • 另一个线程调用这个对象的notifyAll()方法;
  • 过了wait(long time)规定的超时时间,如果传入0就是永久等待;
  • 线程自身调用了interrupt()。

1.2.1.1. 另一个线程调用这个对象的notify()方法且刚好被唤醒的是本线程

package threadcoreknowlodge.threadobjectclasscommonmethods;

/**
 * @author zhl
 * @date 2022/12/23 19:43
 * @description: 展示wait和notify的基本用法 1. 研究代码执行顺序 2. 证明wait释放锁
 */
public class Wait {
    public static Object object = new Object();

    static class Thread1 extends Thread {

        @Override
        public void run() {
            synchronized (object) {
                System.out.println(Thread.currentThread().getName() + "开始执行了");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁。");
            }
        }
    }

    static class Thread2 extends Thread {

        @Override
        public void run() {
            synchronized (object) {
                object.notify();
                System.out.println("线程" + Thread.currentThread().getName() + "调用了notify()");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        Thread.sleep(200);
        thread2.start();
    }
}


执行结果:

Thread-0开始执行了
线程Thread-1调用了notify()
线程Thread-0获取到了锁。

Process finished with exit code 0

需要注意的是另一个线程执行完这个对象的notify()方法之后,该线程并不会立马得到这把锁,而是需要重新去抢锁

1.2.1.2. 另一个线程调用这个对象的notifyAll()方法

package threadcoreknowlodge.threadobjectclasscommonmethods;

/**
 * @author zhl
 * @date 2022/12/24 10:44
 * @description:3个线程,线程1和线程2首先被阻塞,线程3唤醒它们。notify, notifyAll。 start先执行不代表线程先启动。
 */
public class WaitNotifyAll implements Runnable{

    private static final Object resourceA = new Object();

    public static void main(String[] args) throws InterruptedException {
        WaitNotifyAll waitNotifyAll = new WaitNotifyAll();
        Thread thread1 = new Thread(waitNotifyAll);
        Thread thread2 = new Thread(waitNotifyAll);
        thread1.start();
        thread2.start();
        Thread.sleep(200); // 睡眠200ms是等线程1 线程2都抢到锁执行完毕
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA){
                    resourceA.notifyAll();
                    //resourceA.notify();
                    System.out.println("ThreadC notified.");
                }
            }
        }).start();
    }
    @Override
    public void run() {
        synchronized (resourceA){
            System.out.println(Thread.currentThread().getName()+"got resourceA lock.");
            try {
                System.out.println(Thread.currentThread().getName()+"waits to start.");
                resourceA.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"wait to end.");
        }
    }
}

执行结果:

Thread-0got resourceA lock.
Thread-0waits to start.
Thread-1got resourceA lock.
Thread-1waits to start.
ThreadC notified.
Thread-1wait to end.
Thread-0wait to end.

可以看到执行notifyAll()方法,所有线程都会被唤醒,如果执行notify()方法的话,只有单个线程被唤醒,但不确定被唤醒的是线程1还是线程2,这个由JVM决定。另外,之所以在线程1和线程2启动之后,睡眠200ms,是因为要线程1线程2都抢到这把锁,并执行完其中的代码

  • 过了wait(long timeout)规定的超时时间,传入0就是永久等待
package threadcoreknowlodge.threadobjectclasscommonmethods;

/**
 * @author zhl
 * @date 2022/12/23 19:43
 * @description: 展示wait(long time) 超时时间已过,重新获取到锁
 */
public class WaitLongTime {

    public static Object object = new Object();

    static class Thread1 extends Thread {

        @Override
        public void run() {
            synchronized (object) {
                System.out.println(Thread.currentThread().getName() + "拿到了lock.");
                System.out.println(Thread.currentThread().getName() + "开始等待5s的超时时间");
                try {
                    object.wait(5000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程" + Thread.currentThread().getName() + "重新获取到了锁。");
            }
        }
    }



    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        thread1.start();

    }
}

执行结果:

Thread-0拿到了lock.
Thread-0开始等待5s的超时时间
线程Thread-0重新获取到了锁。

Process finished with exit code 0

可以看到线程Thread-0是在等待超时时间过去之后,又重新获取到了锁

  • 线程自身调用了interrupt()
1.2.2 唤醒阶段:

notify()方法是随机唤醒一个线程,notifyAll()方法唤醒所有等待的线程,注意wait()和notify()方法都是在synchronized保护的代码块中执行。

1.2.3 中断阶段:

如果线程被中断,那么会抛出interruptedException,并且释放掉获取的锁。

1.3.wait,notify,notifyAll特点、性质

  • 使用必须先拥有monitor
  • notify()只能唤醒其中一个
  • 属于Object类
  • 同时持有多个锁的情况

1.4.wait原理

wait原理示意图

  • 线程2获取到了锁
  • 线程2执行完毕后释放掉了锁,也就是线程6对应的状态,running->terminated
  • 如果线程2释放掉了锁,也就是线程3对应的状态,相当于线程2执行了wait方法
  • 然后线程会处于Wait Set状态,直到被其他线程notify,然后线程重新竞争锁,竞争的时候处于blocked状态,直到获取锁,并处于Running状态,最后执行完代码,终止线程

1.5.wait()/notify()/notifyAll()常见面试问题

  • 用wait/notify实现生产者消费着设计模式
  • 两个线程交替打印0~200奇偶数
  • 为什么wait()需要在同步代码块内使用,而sleep不需要?
  • 为什么线程通信的方法wait(),niotify()和notifyAll()被定义在Object类里?而sleep定义在Thread类里?
  • wait是属于Object对象的,那么调用Thread.wait会怎么样?
  • 如何选择notify()还是notifyAll()?
  • notifyAll()之后所有线程都会再次抢锁,如果某线程抢夺失败怎么办?

1.5.1. 用wait/notify实现生产者消费者设计模式

这个很重要,请注意

package threadcoreknowlodge.threadobjectclasscommonmethods;

import java.util.Date;
import java.util.LinkedList;

/**
 * @author zhl
 * @date 2022/12/24 14:37
 * @description: 用wait/notify来实现生产者消费者模式
 */
public class ProducerConsumerModel {

    public static void main(String[] args) {
        EventStorage eventStorage = new EventStorage();
        Producer producer = new Producer(eventStorage);
        Consumer consumer = new Consumer(eventStorage);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}
class Producer implements Runnable{
    private EventStorage storage;

    public Producer(EventStorage storage) {
        this.storage = storage;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.put();
        }
    }
}

class Consumer implements Runnable{

    private EventStorage storage;

    public Consumer(EventStorage storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.take();
        }
    }
}


class EventStorage{
    private int maxSize;
    private LinkedList<Date> storage;

    public EventStorage() {
        this.maxSize = 10;
        this.storage = new LinkedList<>();
    }

    /**
     * 生产者生产
     */
    public synchronized void put(){
        // 当storage满了的时候,生产者停止生产,让线程处于waiting
        while (storage.size()==maxSize){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 如果没满,生产者继续生产
        storage.add(new Date());
        System.out.println("仓库里有了" + storage.size() + "个产品。");
        notify();
    }

    /**
     * 消费者消费
     */
    public synchronized void take(){
        // 当storage==0的时候,停止消费
        while (storage.size()==0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 如果不等于0,就进行消费
        Date date = storage.get(0);
        storage.remove(0);
        System.out.println("拿到了 "+date+",现在仓库还剩下"+storage.size());
        notify();
    }
}

1.5.2. 两个线程交替打印0~200奇偶数

这个有2种实现方式,我们分别进行演示,并进行优缺点的比较

1.5.2.1 用synchronized实现

package threadcoreknowlodge.threadobjectclasscommonmethods;

/**
 * @author zhl
 * @date 2022/12/24 15:14
 * @description: 两个线程交替打印0~100的奇偶数,用synchronized关键字实现
 * 缺点:效率不高因为两个线程要不停的抢锁,且同时只有一个线程在执行
 */
public class WaitNotifyPrintOddEvenSyn {

    private  static int count =0;
    private static  final Object lock = new Object();

    public static void main(String[] args) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (count<100){
                    synchronized (lock){
                        if (count%2==0){
                            System.out.println(Thread.currentThread().getName()+":"+count++);
                        }
                    }
                }
            }
        },"偶数").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (count<100){
                    synchronized (lock){
                        if (count%2==1){
                            System.out.println(Thread.currentThread().getName()+":"+count++);
                        }
                    }
                }
            }
        },"奇数").start();
    }
}

说明:这种方式虽然也能实现交替打印,但是效率不高,因为两个线程在不停的争抢锁,不建议使用这种方式。但是这种方式也有一定的优化空间,比如我们在判断是奇偶数的时候,可以使用位运算,我们可以将count%2==0换成(count & 1 ) ==0

1.5…2.2 用wait/notify实现

package threadcoreknowlodge.threadobjectclasscommonmethods;

/**
 * @author zhl
 * @date 2022/12/24 15:09
 * @description: 两个线程交替打印奇偶数 wait/notify
 */
public class WaitNotifyPrintOddEveWait {

    public static void main(String[] args) {
        TurningRunner turningRunner = new TurningRunner();
        new Thread(turningRunner, "偶数").start();
        new Thread(turningRunner, "奇数").start();
    }
}

//1. 拿到锁,我们就打印
//2. 打印完,唤醒其他线程,自己就休眠
class TurningRunner implements Runnable {
    private static int count = 0;
    private static final Object lock = new Object();

    @Override
    public void run() {
        while (count <= 100) {
            synchronized (lock) {
                //拿到锁就打印
                System.out.println(Thread.currentThread().getName() + ":" + count++);
                lock.notify();
                if (count <= 100) {
                    try {
                        //如果任务还没结束,就让出当前的锁,并休眠
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

说明这种方式不需要抢锁,所以会快一些

1.5.3. 为什么wait()需要在同步代码块内使用,而sleep不需要?

主要是为了让通信变得可靠,防止死锁或永久等待的发生,如果没有synchronized 的保护,那么wait和notify就可以随意的切换,sleep只和自己相关,所以不需要放在synchronized 代码块内

1.5.4. 为什么线程通信的方法wait(),niotify()和notifyAll()被定义在Object类里?而sleep定义在Thread类里?

首先wait(),niotify()和notifyAll()是锁级别的操作,而锁是绑定对象的,我们要求锁是对于每个对象都是适用的,所以将被定义在了Object类里

1.5.5. wait是属于Object对象的,那么调用Thread.wait会怎么样?

相当于没有调用,因为调用wait之后,会马上调用notify

1.5.6. 如何选择notify()还是notifyAll()?

如果是唤醒单个线程可以使用notify(),如果是多个线程可以使用notifyAll()

1.5.7. notifyAll()之后所有线程都会再次抢锁,如果某线程抢夺失败怎么办?

如果某线程抢夺失败,那么就会回到Blocked状态,等待某线程释放锁,重新抢夺

2.线程的6种状态

线程的6种状态

  • 创建好新城处于New状态
  • 执行start方法之后就处于Running状态
  • 线程执行完代码块之后处于Terminated状态
  • 当线程在获取到monitor 锁之前,也就是进入synchronized关键字修饰的代码块或方法之前处于Blocked状态,一旦线程拿到了monitor锁之后就处于了Running状态
  • 一旦线程执行了Object.wait()/Thread.join()等方法,线程就会进入wait状态,直到其他线程执行了相同锁的notify()方法

需要补充的是:

  • 线程从Object.wait()状态刚被唤醒时,通常不能立刻抢到monitor锁,那就会从waiting状态先进入Blocked状态,抢到锁之后再转到Running状态。
  • 如果发生异常,可以直接跳到Terminated状态,比如可以从waiting()状态直接到Terminated状态

3.sleep方法详解

3.1. 不释放锁

sleep方法的特点是不释放锁,无论是lock还是synchronized ,只有等sleep时间到了之后,才释放锁

3.1.1 展示线程sleep的时候不释放synchronized的monitor

package threadcoreknowlodge.threadobjectclasscommonmethods;

/**
 * @author zhl
 * @date 2022/12/24 17:04
 * @description: 展示线程sleep的时候不释放synchronized的monitor,等sleep时间到了以后,正常结束后才释放锁
 */
public class SleepDontReleaseMonitor implements Runnable{
    public static void main(String[] args) {
        SleepDontReleaseMonitor monitor = new SleepDontReleaseMonitor();
        new Thread(monitor).start();
        new Thread(monitor).start();
    }
    @Override
    public void run() {
        sync();
    }

    public synchronized void sync(){
        System.out.println(Thread.currentThread().getName()+" 获取到了monitor。");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" 退出了同步代码块。");
    }
}

执行结果

Thread-0 获取到了monitor。
Thread-0 退出了同步代码块。
Thread-1 获取到了monitor。
Thread-1 退出了同步代码块。

可以看出Thread-0执行完了之后才轮到Thread-1,所以线程在sleep的时候并没有释放锁

3.1.2 演示sleep不释放lock(lock需要手动释放)

package threadcoreknowlodge.threadobjectclasscommonmethods;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author zhl
 * @date 2022/12/24 17:13
 * @description: 演示sleep不释放lock(lock需要手动释放)
 */
public class SleepDontReleaseLock implements Runnable{

    private static final Lock lock = new ReentrantLock();
    @Override
    public void run() {
        lock.lock();
        System.out.println("线程"+Thread.currentThread().getName()+" 获取到了锁");
        try {
            Thread.sleep(5000);
            System.out.println("线程" + Thread.currentThread().getName() + " 睡眠后已经苏醒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally { // 最后一定要释放锁
            lock.unlock();
        }

    }

    public static void main(String[] args) {
        SleepDontReleaseLock sleepDontReleaseLock = new SleepDontReleaseLock();
        new Thread(sleepDontReleaseLock).start();
        new Thread(sleepDontReleaseLock).start();
    }
}

执行结果

线程Thread-0 获取到了锁
线程Thread-0 睡眠后已经苏醒
线程Thread-1 获取到了锁
线程Thread-1 睡眠后已经苏醒

Process finished with exit code 0

同样可以看到线程在sleep之后,也没有释放lock

3.2. 响应中断

  • 抛出InterruptedException
  • 清除状态
package threadcoreknowlodge.threadobjectclasscommonmethods;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author zhl
 * @date 2022/12/24 17:42
 * @description: 每个1秒钟输出当前时间,被中断,观察。
 * Thread.sleep()
 * TimeUnit.SECONDS.sleep()
 */
public class SleepInterrupted implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new SleepInterrupted());
        thread.start();
        Thread.sleep(5000);
        thread.interrupt();
    }
    @Override
    public synchronized void run() {
        if (!Thread.interrupted()){
            for (int i = 0; i < 10; i++) {
                System.out.println(new Date());
                try {
                    Thread.sleep(1000);
                    // TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    System.out.println("我被中断了!");
                    e.printStackTrace();
                }
            }
        }
    }
}

Sat Dec 24 18:08:12 CST 2022
Sat Dec 24 18:08:13 CST 2022
Sat Dec 24 18:08:14 CST 2022
Sat Dec 24 18:08:15 CST 2022
Sat Dec 24 18:08:16 CST 2022
我被中断了!
Sat Dec 24 18:08:17 CST 2022
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at threadcoreknowlodge.threadobjectclasscommonmethods.SleepInterrupted.run(SleepInterrupted.java:27)
	at java.lang.Thread.run(Thread.java:748)
Sat Dec 24 18:08:18 CST 2022
Sat Dec 24 18:08:19 CST 2022
Sat Dec 24 18:08:20 CST 2022
Sat Dec 24 18:08:21 CST 2022

Process finished with exit code 0

可以看到在子线程睡眠的时候响应了中断,抛出了InterruptedException异常,并且当前线程清除了状态

一句话总结sleep就是:sleep可以让线程进入Waiting状态,并且不占用CPU资源,但是不释放锁,直到规定时间后执行,休眠期间如果被中断,会抛出异常,并清除中断状态

3.3. sleep常见面试问题

3.3.1. wait()/notify()、sleep异同
相同:

  • 阻塞
  • 响应中断

不同:

  • 同步方法中
  • 释放锁
  • 指定时间
  • 所属类

4. join方法详解

4.1. join方法作用、用法

因为新的线程加入我们,所以我们要等待他执行完再出发
注意:是主线程等待子线程

4.2. 代码演示

4.2.1. 普通用法

主要演示的是主线程等待子线程运行完毕,才开始执行自己的代码

package threadcoreknowlodge.threadobjectclasscommonmethods;

/**
 * @author zhl
 * @date 2022/12/24 18:40
 * @description:  演示join,注意语句输出顺序,会变化。
 */
public class Join {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
            }
        });

        thread1.start();
        thread2.start();
        System.out.println("开始等待子线程运行完毕");
        thread1.join();
        thread2.join();
        System.out.println("所有子线程执行完毕");
    }
}

执行结果:

开始等待子线程运行完毕
Thread-1执行完毕
Thread-0执行完毕
所有子线程执行完毕

Process finished with exit code 0

如果我们将 thread1.join(); thread2.join(); 进行注释,来一起看看结果

执行结果:

开始等待子线程运行完毕
所有子线程执行完毕
Thread-0执行完毕
Thread-1执行完毕

Process finished with exit code 0

可以看到先执行了主线程的代码,然后才执行的子线程的代码

4.2.2. 遇到中断

首先我们在子线程中中断主线程,但是子线程还是会继续运行,这种做法是不可取的,我们来演示一下

package threadcoreknowlodge.threadobjectclasscommonmethods;

/**
 * @author zhl
 * @date 2022/12/24 18:50
 * @description: 演示join期间被中断的效果
 */
public class JoinInterrupt {
    public static void main(String[] args) {

        Thread mainThread = Thread.currentThread();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // TODO 在子线程中 中断主线程
                    mainThread.interrupt();
                    Thread.sleep(5000);
                    System.out.println("线程"+Thread.currentThread().getName()+" finished.");
                } catch (InterruptedException e) {
                    System.out.println("子线程中断");
                }
            }
        });

        thread.start();
        System.out.println("等待子线程运行完毕.");
        try {
            thread.join();
        } catch (InterruptedException e) {
            System.out.println("线程"+Thread.currentThread().getName()+" 中断了。");
            e.printStackTrace();
        }
        System.out.println("子线程已经运行完毕.");
    }
}

执行结果:

等待子线程运行完毕.
线程main 中断了。
子线程已经运行完毕.
java.lang.InterruptedException
	at java.lang.Object.wait(Native Method)
	at java.lang.Thread.join(Thread.java:1252)
	at java.lang.Thread.join(Thread.java:1326)
	at threadcoreknowlodge.threadobjectclasscommonmethods.JoinInterrupt.main(JoinInterrupt.java:29)
线程Thread-0 finished.

Process finished with exit code 0

可以看到主线程中断之后,子线程还在继续运行,那么我们该如何解决呢?
我们应该让子线程也中断,只需要在抓取到主线程中断之后,将中断进行传递
也就是子线程也进行中断 thread.interrupt();

4.2.3. join期间,线程是什么状态?waiting
package threadcoreknowlodge.threadobjectclasscommonmethods;

/**
 * @author zhl
 * @date 2022/12/24 19:39
 * @description: 先join再mainThread.getState()
 * 通过debugger看线程join前后状态的对比
 */
public class JoinThreadState {
    public static void main(String[] args) throws InterruptedException {
        Thread mainThread = Thread.currentThread();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    // TODO 用子线程获取主线程状态
                    System.out.println("线程"+mainThread.getName()+"状态:"+mainThread.getState());
                    System.out.println("Thread-0运行结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        });

        thread.start();
        System.out.println("等待子线程运行完毕");
        thread.join();
        System.out.println("子线程运行完毕");
    }
}

执行结果:

等待子线程运行完毕
线程main状态:WAITING
Thread-0运行结束
子线程运行完毕

Process finished with exit code 0

4.3. 原理

4.3.1. 源码
    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }
4.3.2. 分析

可以看到join底层调用了wait()方法,当millis == 0的时候,调用了wait(0),我们知道wait(0)是永久等待的意思,但是这里并没有看到任何notify(),那它是怎么唤醒线程的呢?这个是有JVM底层实现的。
只需要知道底层每个Thread执行完run()代码之后,会调用notifyAll()的方法

4.3.3. 等价
package threadcoreknowlodge.threadobjectclasscommonmethods;

/**
 * @author zhl
 * @date 2022/12/24 19:56
 * @description:  通过讲解join原理,分析出join的代替写法
 */
public class JoinPrinciple {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
            }
        });

        thread.start();
        System.out.println("开始等待子线程运行完毕");
        thread.join();
//        synchronized (thread) {
//            thread.wait();
//        }
        System.out.println("所有子线程执行完毕");
    }
}

执行结果:

开始等待子线程运行完毕
Thread-0执行完毕
所有子线程执行完毕

Process finished with exit code 0

join等同于 synchronized (thread) { thread.wait(); }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值