线程同步(一)

1 join
threadA.join(),如果threadA没有执行完毕(A线程任然是alive状态),则让当前线程(threadB)等待wait
Thread threadA = new Thread(new Runnable() {       
      public void run() {//......}
 });
Thread threadB = new Thread(new Runnable() {
     public void run() {
          threadA.join();// B线程中执行A线程join
     }
});
threadA.start();
// [1]
threadB.start();

(1)start是一个非阻塞方法,尽管在main方法中,先执行threadA.start(),后执行threadB.start(),但这仅仅是提请虚拟机执行线程,但究竟A先执行还是B先执行却不一定,可能线程A先执行,也可能线程B先执行
(2)如果在[1]处增加很长延时,实际上A已经执行完毕了,A线程已经销毁,则线程B执行时候不会等待线程A
(3)若最后改成:
threadB.start();
Thread.sleep(10000)
threadA.start();
由于线程B执行时候,线程A根本就没有启动(即不处于alive),所以线程B也不会等待线程A执行

2 wait notify notifyAll 和 synchronized
(1)wait、notify必须写到synchronized的块中(wait/notify/synchronized成对出现,否则报错:IllegalMonitorStateException)
(2)这三个方法被所有的Object共有。使同一个实例object存在在不同的线程,如果线程A执行object.wait(),则线程A释放syncronized锁,线程A进入wait状态;线程B获得syncronized锁,执行object.notify(),则换起因调用object.wait()的线程A,线程A从wait状态退出,进入就绪态
(3)JVM规定当调用object的wait/notify的时,检查当前线程是否持有object对象锁,不满足抛出IllegalMonitorStateException
(4)一旦调用wait,当前线程就释放了对象锁,就不能阻止其它线程进入同步块
(5)当线程A被其它线程B的notify唤醒时,线程A并不能立即从wait()方法返回,而是等线程B退出同步块后才能从wait()返回
Object obj = new Object();
Thread threadA = new Thread(new Runnable() {   
            public void run() {
               synchronized(obj) {
                        obj.wait();
                }
            }
        });
Thread threadB = new Thread(new Runnable() {
            public void run() {
                synchronized(obj) {
                    obj.notifyAll();
                }
            }
        });
threadA.start();
threadB.start();

(6)通知丢失和假唤醒
通知丢失:由于无法100%保证上面的threadB和threadA执行顺序(start调用顺序与实际的线程执行顺序无关,synchronized不能保证执行顺序)。线程A调用wait错过了线程B的notify通知,线程B将永远处于wait状态;
假唤醒:在没有调用notify的情况下,线程可能莫名的退出wait状态;
改进类来优化这个问题,设计准则是wait必须要先于notify调用

public class MyWaitNotify {
    boolean isNotifyed = false;
    Object obj = new Object();
    void doWait() throws InterruptedException {
        synchronized(obj) {
           // 如果先于doNotify执行,isNotifyed为false,表示没有通知过,方可能进入wait,等待notify;
           // 如果isNotifyed=true,表示实际上信号已经通知过了,只不过我不小心错过了,所以不需要等待,直接执行即可,也是满足业务逻辑。
           // 使用while而不使用if的作用是当线程多次假唤醒时,isNotifyed始终为false,保证线程可重新进入wait状态
            while(!isNotifyed) {
                obj.wait();
            }
            isNotifyed=false;
        }
    }   
    void doNotify() {
        synchronized(obj) {
            isNotifyed=true;
            obj.notifyAll();
        }
    }
}

(7)wait、notify必须写在synchronized块中的另一个意义还在于并发时候防止通知信号notify的丢失

class BlockingQueue {
    Queue<String> buffer = new LinkedList<String>();

    public void give(String data) {
        buffer.add(data);//[1]
        notify(); //[2]
    }

    public String take() throws InterruptedException {
       while (buffer.isEmpty()/*[3]*/) {
           wait();//[4]
        }
        return buffer.remove();// 消费消息
    }
}
阻塞队列,线程A生产者执行give,线程B消费者执行take,由于并发原因,之际执行顺序:[3]->[1]->[2]->[4],即先执行notify()再执行wait(),那么如果没有新的give,线程B将永远处于wait,且buffer中有一条消息永远得不到消费(不考虑假唤醒)
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值