线程间的通信及等待之wait()与notify() | notifyAll() 与 join()方法

总结

1:wait()方法

1:在调用此方法之前,线程必须获得该对象的对象级别的锁,及只能在同步方法或同步代码块中使用。
2: 调用此方法后线程将从运行状态进入阻塞队列,同时释放对象锁(与sleep()不同)。直到该对象调用notify() | notifyAll() 后该线程才能够拥有重新竞争锁的机会。
若线程处于wait状态时执行了interrupt()方法,线程会抛出 InterruptedExcuption 异常

2:wait(long)方法

等待时间内是否有其他线程对锁进行唤醒,若没有,则到时间后自动唤醒。

3: notify()

  • 在调用此方法之前,线程必须获得该对象的对象级别的锁,及只能在同步方法或同步代码块中使用。
  • 该方法用来通知那些可能等待该对象的对象锁的线程,如果有多个等待线程,则由线程规划器随机挑选出其中一个程wait状态的线程,使之等待获得该对象的锁
  • 调用该方法后,当前线程不会立即释放锁,需要当当前notify() 方法所处的同步代码块中的方法全部执行完后,当前线程才会释放锁
  • 使用此方法要注意 唤醒同类 例如生产者消费者模式中,消费者消费完成后,正常情况下执行此方法是要唤醒生产者,但是此方法唤醒阻塞线程是随机的,所有有可能唤醒的是同样处于阻塞状态的消费者线程。

4:notifyAll()

1 :其作用与notify()相同,不同的是notify()一次只能唤醒一个线程,而notifyAll()则可以唤醒当前对象所对应的全部阻塞线程。
2:join()方法

5:join()方法

1:在很多情况下主线程结束的时间都早于子线程,若想要等到子线程结束在结束主线程,则可以用jion()方法。 方法join()的作用便是等待调用 join的线程对象销毁。

2:在join的过程中,若当前线程对象被中断,则当前线程抛出异常。

3: 执行join方法后,线程会进入等待(WAITING)状态

4: join(long)与sleep(long)的区别:

  • 方法join的功能在内部是使用wait(long)的方法来实现的,所有此方法具有释放锁的功能。
  • sleep(long)不具备释放锁的功能。

6:wait()与notify()方法实践

示例一

public class WaitNotifyThread {

    private static volatile Object resA = new Object();
    private static volatile Object resB = new Object();

    public static void main(String[] args) {

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resA){
                    System.out.println("ThreadA 获取到锁  resA");

                    synchronized (resB){
                        System.out.println("ThreadA 获取到锁  resB");

                        try {
                            System.out.println("ThreadA 释放锁  resA");
                            /**
                             * 执行wait() | notify()方法前,该线程必须获得该对象的对象级别的锁
                             * 否则抛异常
                             */
                            resA.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });



        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resA){
                    System.out.println("ThreadB 获取到锁  resA");

                    System.out.println("线程ThreadB 中释放resA锁");
                    //调用此方法后,此线程依旧不能获得resB对象。
                    //因为调用此方法后,当前线程不会立马释放该对象的锁,呈现wite状态的线程也并不能
                    //立马获取该对象的锁,要等到执行notify()方法的线程将程序执行完。
                    //因此接下来的synchronized (resB)因无法获得对象而无法执行。整个系统处于阻塞状态。
                    resA.notify();

                    synchronized (resB){
                        System.out.println("ThreadB 获取到锁  resB");

                        try {
                            System.out.println("ThreadB 释放锁  resA");
                            resA.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

示例二
利用wait和notify实现两线程循环打印奇偶数


/**
 * 两个线程循环打印0到100的奇偶数
 */
public class WaitTest {
    private static final Object lock = new Object();
    private static int count =0;

    public static void main(String[] args) throws InterruptedException {
        new Thread(new HasAnEvenNumber(lock,count),"偶数").start();
        //Thread.sleep(100);
       new Thread(new HasAnEvenNumber(lock,count),"奇数").start();
    }
}

class HasAnEvenNumber implements Runnable{
    private static int count;
    private Object lock;

    public HasAnEvenNumber(Object lock,int count) {
        this.lock = lock;
        this.count = count;
    }

    @Override
    public void run() {
        synchronized (lock){
            while (count <= 100){
                System.out.println(Thread.currentThread().getName() +"  " + count++);
                lock.notify();
                if (count<=100){
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

生产者与消费者

一个生产者与一个消费者

public class Producers_And_Consumers {

    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        P_Thread p = new P_Thread(myStack);
        C_Thread c = new C_Thread(myStack);

        Thread threadP = new Thread(p);
        threadP.start();


        Thread threadC = new Thread(c);
        threadC.start();
    }
}

/**
 * 生产者
 */
class MyStack{

    ArrayList<String> arrayList = new ArrayList<>();

    synchronized public void set(){
        if (arrayList.size() == 1){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        arrayList.add("张三");
        /** 生产后通知消费者消费*/
        this.notify();
        System.out.println("set : "+arrayList.size());
    }
    
    synchronized public void get(){
        if (arrayList.size() == 0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("get : "+arrayList.get(0));
        arrayList.remove(0);
        /** 消费后通知生产者生产*/
        this.notify();
    }
}

/**
 * 多线程之生产者调用
 */
class P_Thread implements Runnable{

    private MyStack myStack;

    public P_Thread(MyStack myStack){
        this.myStack = myStack;
    }

    @Override
    public void run() {
        while (true){
            myStack.set();
        }
    }
}


class C_Thread implements Runnable{

    private MyStack myStack;

    public C_Thread(MyStack myStack){
        this.myStack = myStack;
    }
    @Override
    public void run() {
        while (true){
            myStack.get();
        }

    }
}

出现假死的情况

一个生产者多个消费者的情况

改变上述代码

 public static void main(String[] args) {
        MyStack myStack = new MyStack();
        P_Thread p = new P_Thread(myStack);
        C_Thread c = new C_Thread(myStack);

        Thread threadP = new Thread(p);
        threadP.start();


        Thread threadC1 = new Thread(c);
        Thread threadC2 = new Thread(c);
        Thread threadC3 = new Thread(c);
        Thread threadC4 = new Thread(c);
        Thread threadC5 = new Thread(c);
        threadC1.start();
        threadC2.start();
        threadC3.start();
        threadC4.start();
        threadC5.start();

    }

此时看控制台
在这里插入图片描述
控制台并未在打印,及生产者与消费者不再执行,同时线程并未停止,此时线程便处于阻塞状态。
出现这种情况的原因便是 唤醒了同类线程

解释: 当线程处于等待状态时,生产者被某个线程唤醒,进行生产。生产完成后随机唤醒处于阻塞状态的消费者。消费者执行 arrayList.remove(0);进行消费。消费完成后执行notify()方法,但是此时唤醒的并不是生产者,而是同类的消费者,接下来唤醒的消费者继续执行 arrayList.remove(0);方法,但此时集合为空,所以便出现数组下标越界的情况,抛出异常。消费者线程暂停,同时生产者线程因为未被唤醒处于阻塞状态。

解决方式: 将生产者与消费者中的 if (arrayList.size() == 1)换成while循环

还有一种假死的情况是 消费者同类唤醒,造成生产者始终处于未被消费的状态,从而造成假死
解决方式:将唤醒的notify()换成notifyAll()

join()方法代码展示

public class JoinThread {
    public static void main(String[] args) throws InterruptedException {
        Join join = new Join();
        Thread thread = new Thread(join);

        thread.start();

        thread.join();

        System.out.println("主线程 ···");
    }
}

class Join implements Runnable{

    @Override
    public void run() {
        System.out.println("子线程 begin~~~");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值