Java多线程wait,notify以及同步锁的运用实例

一次性开多个线程打印10次ABC的小Demo,如何保证线程执行的有序性,以下面这个Demo来做说明:

package com.lyt.usermanage.test;

public class MyThreadTest5 implements Runnable {

    private String name;
    private Object lastThread;
    private Object self;

    public MyThreadTest5(String name,Object lastThread,Object self){
        this.name = name;
        this.lastThread = lastThread;
        this.self = self;
    }

    @Override
    public void run() {
        int count  = 10;
        while(count>0){
            synchronized(lastThread){
                synchronized(self){
                    System.out.print(name);
                    count --;
                    /*唤醒在等待该对象同步锁的线程(只唤醒一个,如果有多个在等待),注意的是在调用此方法的时候,
                    并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
                    调用任意对象的notify()方法则导致因调用该对象的 wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。*/
                    self.notify();
                }

                try {
                    //调用任意对象的 wait() 方法导致该线程阻塞,该线程不可继续执行,并且该对象上的锁被释放。
                    lastThread.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}
package com.lyt.usermanage.test;

public class ThreadTest {

    public static void main(String[] args) throws InterruptedException {

        System.out.println(Thread.currentThread().getName()+"主线程运行开始!");

        Object a = new Object();     
        Object b = new Object();     
        Object c = new Object();     
        MyThreadTest5 pa = new MyThreadTest5("A", c, a);     
        MyThreadTest5 pb = new MyThreadTest5("B", a, b);     
        MyThreadTest5 pc = new MyThreadTest5("C", b, c);     


        new Thread(pa).start();  
        Thread.sleep(100);  //确保按顺序A、B、C执行  
        new Thread(pb).start();  
        Thread.sleep(100);    
        new Thread(pc).start();     
        Thread.sleep(100); 

    }
}

先来解释一下其整体思路,从大的方向上来讲,该问题为三线程间的同步唤醒操作,
主要的目的就是ThreadA->ThreadB->ThreadC->ThreadA循环执行三个线程。为了控制线程执行的顺序,
那么就必须要确定唤醒、等待的顺序,所以每一个线程必须同时持有两个对象锁,才能继续执行。
一个对象锁是prev,就是前一个线程所持有的对象锁。还有一个就是自身对象锁。
主要的思想就是,为了控制执行的顺序,必须要先持有prev锁,也就前一个线程要释放自身对象锁,再去申请自身对象锁,
两者兼备时打印,之后首先调用self.notify()释放自身对象锁,唤醒下一个等待线程,再调用prev.wait()释放prev对象锁,
终止当前线程,等待循环结束后再次被唤醒。运行上述代码,可以发现三个线程循环打印ABC,共10次。程序运行的主要过程就是A线程最先运行,
持有C,A对象锁,后释放A,C锁,唤醒B。线程B等待A锁,再申请B锁,后打印B,再释放B,A锁,唤醒C,线程C等待B锁,再申请C锁,后打印C,再释放C,B锁,唤醒A。
看起来似乎没什么问题,但如果你仔细想一下,就会发现有问题,就是初始条件,三个线程按照A,B,C的顺序来启动,按照前面的思考,A唤醒B,B唤醒C,C再唤醒A。
但是这种假设依赖于JVM中线程调度、执行的顺序。

具体流程:

我们看到A B C三个线程共用三个锁对象a b c,由于在主线程加了休眠,那么三个线程的执行顺序就有了ABC的保障;A线程进来run方法后打印A,然后唤醒需要a对象同步锁的线程并释放持有的a对象锁,这个时候只有B线程在申请a锁,于是B线程被唤醒,然后A线程调用wait方法进入阻塞状态并释放c锁;B线程进行和A线程一样的步骤,进来后打印B然后释放b锁并唤醒等待b对象的线程,也就是C线程,然后调用wait方法进入阻塞状态并释放a对象;C线程重复上面的操作,唤醒了等待c锁对象的A线程,又是一个循环,因为new Thread(pa).start(); 方法里面不是公用的一个对象,所以各个线程都要跑10次,达到我们的需求10次ABC的输出。

个人理解是这样,如果有不足请大家多留言改进,技术只有交流才能深刻。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值