多线程学习总结(十)——线程安全之线程间的通信深入

    声明:文章内容全都是自己的学习总结,如有不对的地方请大家帮忙指出。有需要沟通交流的可加我QQ群:425120333

    wait()方法只能在同步块中使用,如果只是在代码中写了wait()(相当于是this.wait()),这种情况只能是在对类对象进行同步时,才不会有问题,
如果你同步的是另一个对象,而调用了是别的对象的wait()那就会出错,达不到线程通信的目的。也就是是同步的对象和调用wait()的方法要是同一个对象
(所有类都是Object对象的子类,而wait()方法是Object类的)。
    看下面一个例子来理解下这个概念:
public class TestWait {
    public static void main(String[] args) throws Exception {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();

        new Thread(new PrintA(a, b)).start();
        //保证打印A先运行
        TimeUnit.MILLISECONDS.sleep(50);
        new Thread(new PrintB(b, c)).start();
        //保证打印B先运行
        TimeUnit.MILLISECONDS.sleep(50);
        new Thread(new PrintC(c, a)).start();

    }
}

class PrintA implements Runnable {

    private Object obj_1;
    private Object obj_2;

    public PrintA(Object obj_1, Object obj_2) {
        this.obj_1 = obj_1;
        this.obj_2 = obj_2;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            synchronized (obj_1) {
                synchronized (obj_2) {
                    System.out.print("A-->");
                    obj_2.notify();
                }
                try {
                    obj_1.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println();
        }

        //保证打印B线程能正常结束
        synchronized (obj_2) {
            obj_2.notify();
        }
    }
}

class PrintB implements Runnable {

    private Object obj_1;
    private Object obj_2;

    public PrintB(Object obj_1, Object obj_2) {
        this.obj_1 = obj_1;
        this.obj_2 = obj_2;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            synchronized (obj_1) {
                synchronized (obj_2) {
                    System.out.print("B-->");
                    obj_2.notify();
                }
                try {
                    obj_1.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        //保证打印B线程能正常结束
        synchronized (obj_2) {
            obj_2.notify();
        }
    }
}

class PrintC implements Runnable {

    private Object obj_1;
    private Object obj_2;

    public PrintC(Object obj_1, Object obj_2) {
        this.obj_1 = obj_1;
        this.obj_2 = obj_2;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            synchronized (obj_1) {
                synchronized (obj_2) {
                    System.out.print("C");
                    obj_2.notify();
                }
                try {
                    obj_1.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

控制台输出:
A–>B–>C
A–>B–>C
A–>B–>C
A–>B–>C
A–>B–>C
这段代码的目的也就是按顺序打印ABC,并且打印五次。看控制台输出的结果就知道已经实现了该功能,但是输出不是主要的,关键是要理解其中的含义,
我给大家讲下整个类的代码(当然你都看懂了,可以跳过),三个打印线程基本上是类似的,代码变动不大,其中写了for循环是为了多打印
几次保证准确,每个打印线程都是有两个Object类型的变量,run()方法中写的是先同步第一个变量,再同步第二个变量,输出打印内容,
唤醒阻塞在第二个变量的线程,然后本线程阻塞在第一个变量上。每个打印类都是这个逻辑,但是对每个打印线程来说,第一个变量和
第二个变量的值都是不一样的,主类的main中是这样写的
new PrintA(a, b)
new PrintB(b, c)
new PrintC(c, a)
也就是说对于打印A线程来说第一变量是a,第二个变量是b;对于打印B线程来说第一变量是b,第二个变量是c;对于打印C线程来说第一变量是c,
第二个变量是a。而整个执行逻辑是先打印A,再打印B,再打印C(TimeUnit.MILLISECONDS.sleep(50);这句的作用)。
那整个流程串起来理解就是,首先打印A线程执行了,获取到了a,b两个的锁(如果有别的线程持有这两个的锁就要等待),
输出“A–>”唤醒阻塞在b上的一个线程(第一次的时候没有线程阻塞在b上),然后打印A线程阻塞在a上(一直等到有线程唤醒)。
然后打印B线程获取到了b,c两个的锁(如果有别的线程持有这两个的锁就要等待),输出“B–>”唤醒阻塞在c上的一个线程
(第一次的时候没有线程阻塞在c上),然后打印B线程阻塞在b上(一直等到有线程唤醒)。
然后打印C线程获取到了c,a两个的锁(如果有别的线程持有这两个的锁就要等待),输出“B”唤醒阻塞在a上的一个线程(也就是打印A线程)
,然后打印C线程阻塞在C上(一直等到有线程唤醒)。整个流程就这样循环执行5次,然后线程结束。因为整个流程中打印B线程要执行,
必须要等打印A输出后才能唤醒,同理打印C要等打印B输出后唤醒,打印A要等打印C输出后唤醒(第一次除外),所以保证了输出的顺序性。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值