java 知识点Java 杂谈

java 多线程通信

2019-09-24  本文已影响0人  Tim在路上

线程间的通信又称为进程内的通信

wait和notify是Object中的方法

wait(0) 0代表永不超时, Object的wait方法会导致当前的线程陷入阻塞状态,直到其他线程notify或notifyAll 才能将其唤醒,或者阻塞时间到而自动唤醒.

当前线程执行对象的wait方法之后,将会放弃对monitor的所有权,并进入与对象关联的wait set中,一旦线程执行了wait会释放monitor的所有权

notify 唤醒正在执行wait的方法的线程.
如果某个线程由于执行wait进入阻塞则会被唤醒,被唤醒需要重新获取对象所关联的monitor的lock才能继续执行

wait方法是可中断的方法,当前线程调用了wait方法进入了阻塞状态,其他线程可以使用interrupt方法将其打断,可中断方法在被打断后会收到InterruptedException异常,同时Interupt标识也会被擦除

线程执行了某一个对象的wait方法后会加入到对应的wait set中,notify可以将其唤醒,从wait set中进行弹出,同时中断wait中的线程也会将其唤醒

必须在同步方法中使用wait和notify方法,因为执行wait和notify的前提条件是持有同步方法的monitor的所有权
同步方法的monitor锁,必须与wait 和 notify的方法的对象一致
private final Object MUTEX = new Object();
private synchronized void testWait(){
    try{
        MUTEX.wait();
    }catch(InterruptedException e){
    
    }
}

private synchronized void testNotify(){
    MUTEX.notify();
}

这里同步方法使用的是this 锁,但是wait和notify方法使用的是MUTEX的方法

wait 和 sleep 相似与区别

wait 和 sleep 都可以让当前的线程陷入阻塞,都是可中断的方法
wait 是 object 的方法,而sleep是thread的特有方法
线程在同步方法中执行sleep方法时,并不会释放monitor的锁,而wait方法则会.

多线程通信

notify 是唤醒阻塞线程中的一个,但是notifyAll 可以唤醒全部的阻塞线程,同样的是被唤醒的线程需要争抢monitor的锁.

public void offer(Event event){
    synchronized (eventQueue){
        while(eventQueue.size >= max){
            try{
                console(" the queue is full");
                eventQueue.wait();
            }catch(InterruptedException e){
                
            }
        }
        console("the new event is submitted");
        eventQueue.addLast(event);
        eventQueue.notifyAll();
    }
}

synchronized 关键字缺陷

  1. 无法控制阻塞的时长
  2. 阻塞不可能被中断
  3. synchronized 不能捕获到中断信号
public class BooleanLock implements Lock{
    private Thread currentThread;

    private boolean locked = false;

    private final List<Thread> blockedThreadList = new ArrayList<>();

    @Override
    public void lock() throws InterruptedException {
        synchronized (this){
            // lock方法体使用synchronized包裹,lock完就释放真实的锁 @1
            while (locked){
                 if(!blockedThreadList.contains(Thread.currentThread())){
                     blockedThreadList.add(Thread.currentThread());
                 }
                try {
                    //locked标志为true,当前线程就等待locked为false,自身等待,wait方法释放真实的锁 @2
                    this.wait();
                }catch (InterruptedException e){
                     //这里线程是上面的wait方法被打断会跳出,因为线程是调用wait方法主动阻塞
                     // 而不是在等待synchronized锁释放而陷入阻塞,所以是可以打断的,
                     //所以这里是整个BooleanLock机制的核心!
                    blockedThreadList.remove(Thread.currentThread());
                    throw new InterruptedException(Thread.currentThread().getName() + "被打断");
                }
            }

            //如果走到这里,表示之前没有线程获取到锁,我获取到了
            blockedThreadList.remove(Thread.currentThread());
            //上锁
            locked = true;
            currentThread = Thread.currentThread();
        }
    }

    @Override
    public void lock(long mills) throws InterruptedException,TimeoutException {
        synchronized (this){
            //如果传入的时间不合法,就直接调用没有超时功能的Lock
            if(mills <= 0){
                this.lock();
            }else{
                long lastMills = mills;
                //这里记录一个时间戳,totalMills表示到达这个时刻,如果还没有获取到锁,就报超时
                long totalMills = lastMills + System.currentTimeMillis();
                while (locked){
                    if (lastMills <= 0){
                        throw new TimeoutException(Thread.currentThread().getName() + "超时" + mills + ".mills" + "没有获取到锁");
                    }

                    if(!blockedThreadList.contains(Thread.currentThread())){
                        blockedThreadList.add(Thread.currentThread());
                    }
                    //这里释放了真实的锁
                    this.wait(lastMills);
                    // lastMills表示还需要等待多久时间,而lastMills减少表示两种情况
                    // 第一种: 线程已经wait到时间了,自动唤醒了,这个时候已经过去了lastMills的时间,下面这个操作就把lastMills置0
                    // 第二种: 线程wait没有到时间,被unlock唤醒,但是被唤醒后还是没有抢到锁,于是lastMills被减少,
                    //        继续等待,时间减去已经等待了多长时间
                    lastMills = totalMills - System.currentTimeMillis();
                }
                blockedThreadList.remove(Thread.currentThread());
                locked = true;
                currentThread = Thread.currentThread();
            }
        }

    }

    @Override
    public void unlock() {
        synchronized (this){
            //如果当前线程是获取到锁的线程,就可以去执行释放锁的操作
            if (currentThread == Thread.currentThread()){
                //释放锁
                this.locked = false;
                //唤醒所有正在wait而陷入阻塞的线程,因为locked已经被设置成false,所以可以进入下一轮争抢
                this.notifyAll();
            }
        }
    }

    @Override
    public List<Thread> getLockedThreads() {
        //unmodifiableList表示不可修改,防止阻塞队列被强行主动修改,出现错误
        return Collections.unmodifiableList(blockedThreadList);
    }
}

上一篇下一篇

猜你喜欢

热点阅读