Java 线程间通信

作者: Tinyspot | 来源:发表于2022-09-07 22:18 被阅读0次

    1. 线程之间如何通信

    • 共享内存(隐式通信)
    • 消息传递(显式通信 wait / notify synchronized)

    1.2 阻塞

    • BLOCKED 和 WAITING 的线程都处于阻塞状态,不占用 CPU 时间片
    • BLOCKED 线程会在 Owner 线程释放锁时唤醒
    • WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒,但唤醒后并不意味着立刻获得锁,仍需进入 EntryList 重新竞争

    2. wait() / notify() / notifyAll()

    • 都是 Object 对象的方法,必须获得此对象的锁,才能调用这些方法
    • obj.wait()
    • obj.notify() 唤醒 obj 上任意一个线程
    • obj.notifyAll() 唤醒 obj 上所有正在 WaitSet 等待的线程

    2.1 wait()

    public class Demo {
        // 声明为 final, 表示引用不可变,若引用变了,synchronized 锁的就是不同对象
        static final Object obj = new Object();
        public static void main(String[] args) throws InterruptedException {
            obj.wait();
        }
    }
    

    运行会抛异常 Exception in thread "main" java.lang.IllegalMonitorStateException,因为此时并未获得锁

    public class Demo {
        static final Object obj = new Object();
        public static void main(String[] args) throws InterruptedException {
            synchronized (obj) {
                obj.wait();
            }
        }
    }
    

    使用 synchronized 先获得锁,成为该锁的 owner, 然后再调用 wait()

    2.2 wait(long n)

    有时限的等待,到 n 毫秒后结束等待,或被 notify()

        public final void wait() throws InterruptedException {
            wait(0);
        }
    
        public final native void wait(long timeout) throws InterruptedException;
        public final void wait(long timeout, int nanos) throws InterruptedException {}
    

    示例

    static final Object obj = new Object();
    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (obj) {
                log.debug("start...");
                try {
                    obj.wait(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.debug("do something...");
            }
        }).start();
    }
    

    2.3 notify() / notifyAll()

    @Slf4j
    public class Demo {
    
        static final Object obj = new Object();
    
        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                synchronized (obj) {
                    log.debug("run...");
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.debug("do something...");
                }
            }).start();
    
            new Thread(() -> {
                synchronized (obj) {
                    log.debug("run...");
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.debug("do something...");
                }
            }).start();
    
            TimeUnit.SECONDS.sleep(1);
            synchronized (obj) {
                // obj.notify();
                obj.notifyAll();
            }
        }
    }
    

    3. wait() vs sleep()

    • wait(), notify() 和 notifyAll() 被定义在 Object 类,而 sleep() 定义在 Thread 类
    • wait() 要在同步代码块内使用(要先获取对象锁,和 synchronized 一起用),而 sleep() 不需要
    • wait() 在等待的时候会释放对象锁,而 sleep() 在睡眠的同时,不会释放对象锁
    public class Thread implements Runnable {
        public static native void sleep(long millis) throws InterruptedException;
    }
    
    public class Object {
        public final native void wait(long timeout) throws InterruptedException;
    }
    

    3.1 验证 sleep() 不释放锁

    @Slf4j
    public class Demo {
        static final Object obj = new Object();
        public static void main(String[] args) throws InterruptedException {
            new Thread(() -> {
                synchronized (obj) {
                    log.debug("start...");
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    log.debug("do something...");
                }
            }).start();
    
            TimeUnit.SECONDS.sleep(1);
    
            // main 线程想获取锁,并执行逻辑
            synchronized (obj) {
                log.debug("run...");
            }
        }
    }
    

    执行结果

    22:16:16.687 [Thread-0] DEBUG com.example.concrete.Demo - start...
    22:16:19.691 [Thread-0] DEBUG com.example.concrete.Demo - do something...
    22:16:19.691 [main] DEBUG com.example.concrete.Demo - run...
    

    相关文章

      网友评论

        本文标题:Java 线程间通信

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