java 多线程通信

作者: Tim在路上 | 来源:发表于2019-09-24 16:08 被阅读0次

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

    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);
        }
    }
    
    

    相关文章

      网友评论

        本文标题:java 多线程通信

        本文链接:https://www.haomeiwen.com/subject/lccbuctx.html