17:sleep与wait在释放锁上的区别

1,300 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

Sleep

sleep是Thread类中的静态方法,调用sleep会使得当前线程睡眠一段时间。睡眠状态开始时放弃对CPU的掌控,并在睡眠持续期间不再抢夺CPU计算资源,但是睡眠状态并不会释放持有的锁资源。可以认为睡眠只是暂时停滞,期间除了线程不做任何动作之外其他特性和正常运行时并没有很大的差别(锁什么的都拿着)。调用sleep需要捕获InterruptedException避免数据期间设置中断标识。

验证

验证sleep期间不会释放锁资源:

public static void main(String[] args) {
   Object lock = new Object();
    long startTime = System.currentTimeMillis();
    Runnable rs1 = new Runnable() {
        
        @Override
        public void run() {
                System.out.println("rs1启动:" + (System.currentTimeMillis() - startTime));
            synchronized (lock) {
                System.out.println("rs1拿到了锁:" + (System.currentTimeMillis() - startTime));
                System.out.println("rs1开始睡眠:" + (System.currentTimeMillis() - startTime));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("rs1睡眠结束:" + (System.currentTimeMillis() - startTime));
            }
            System.out.println("rs1释放了锁:" + (System.currentTimeMillis() - startTime));
            
        }
    };

    Runnable rs2 = new Runnable() {
        
        @Override
        public void run() {
                System.out.println("rs2启动:" + (System.currentTimeMillis() - startTime));
            synchronized (lock) {
                System.out.println("rs2拿到了锁:" + (System.currentTimeMillis() - startTime));
                System.out.println("rs2开始睡眠:" + (System.currentTimeMillis() - startTime));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("rs2睡眠结束:" + (System.currentTimeMillis() - startTime));
            }
            System.out.println("rs2释放了锁:" + (System.currentTimeMillis() - startTime));
            
        }
    };
    Thread ts1 = new Thread(rs1);
    Thread ts2 = new Thread(rs2);
    ts1.start();
    ts2.start();
}

执行结果:

rs1启动:1
rs2启动:1
rs1拿到了锁:2
rs1开始睡眠:2
rs1睡眠结束:1003
rs1释放了锁:1003
rs2拿到了锁:1003
rs2开始睡眠:1003
rs2睡眠结束:2004
rs2释放了锁:2004

解析:rs1和rs2在1ms同时启动,然后rs1在2ms拿到锁并开始睡眠,在1003ms释放了锁,rs2直到1003ms才拿到了锁,说明rs1在睡眠期间并没有释放锁。

wait

wait是Object类的实例方法,调用wait会使得当前线程进入等待状态,只有获取到锁才能调用wait(在同步块内使用),未获得锁时调用wait会抛出异常。等待状态会释放执行wait的锁资源(仅限于执行wait的锁,其他锁资源并不会释放)。wait可以设置等待时间,到达时间自动唤醒,而不需要(notify,notifyAll)。调用wait需要捕获InterruptedException避免数据期间设置中断标识。

验证

验证wait会释放进行wait的操作的锁,但是不会释放其他锁资源:

public static void main(String[] args) {
    Object lock = new Object();
    Object lock2 = new Object();
    long startTime = System.currentTimeMillis();
    Runnable rw1 = new Runnable() {
        
        @Override
        public void run() {
            System.out.println("rw1启动:" + (System.currentTimeMillis() - startTime));
            synchronized (lock) {
                System.out.println("rw1拿到了锁1:" + (System.currentTimeMillis() - startTime));
                synchronized (lock2) {
                    System.out.println("rw1拿到了锁2:" + (System.currentTimeMillis() - startTime));
                    System.out.println("rw1睡眠1000ms");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                    lock.notifyAll();
                    System.out.println("rw1开始等待:" + (System.currentTimeMillis() - startTime));
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("rw1等待结束:" + (System.currentTimeMillis() - startTime));
                    lock.notifyAll();
                }
                System.out.println("rw1释放了锁2:" + (System.currentTimeMillis() - startTime));
            }
            System.out.println("rw1释放了锁1:" + (System.currentTimeMillis() - startTime));
            
        }
    };

    Runnable rw2 = new Runnable() {
        
        @Override
        public void run() {
            System.out.println("rw2启动:" + (System.currentTimeMillis() - startTime));
            synchronized (lock) {
                System.out.println("rw2拿到了锁1:" + (System.currentTimeMillis() - startTime));
                System.out.println("rw2睡眠1000ms");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                lock.notifyAll();
                System.out.println("rw2开始等待:" + (System.currentTimeMillis() - startTime));
                try {
                    lock.wait(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("rw2等待结束:" + (System.currentTimeMillis() - startTime));
                lock.notifyAll();
            }
            System.out.println("rw2释放了锁1:" + (System.currentTimeMillis() - startTime));
            
        }
    };
    Runnable rw3 = new Runnable() {
        
        @Override
        public void run() {
            System.out.println("rw3启动:" + (System.currentTimeMillis() - startTime));
            try {
                Thread.sleep(100); // 睡眠100ms以保证rw1先拿到锁2
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock2) {
                System.out.println("rw3拿到了锁2:" + (System.currentTimeMillis() - startTime));
            }
            System.out.println("rw3释放了锁2:" + (System.currentTimeMillis() - startTime));
            
        }
    };
    Thread tw1 = new Thread(rw1);
    Thread tw2 = new Thread(rw2);
    Thread tw3 = new Thread(rw3);
    tw1.start();
    tw2.start();
    tw3.start();
}

运行结果:

rw1启动:11
rw1拿到了锁111
rw1拿到了锁211
rw1睡眠1000ms
rw2启动:11
rw3启动:12
rw1开始等待:1011
rw2拿到了锁11011
rw2睡眠1000ms
rw2开始等待:2011
rw1等待结束:2011
rw1释放了锁22011
rw3拿到了锁22011
rw3释放了锁22011
rw1释放了锁12011
rw2等待结束:2011
rw2释放了锁12012

解析:可以看到rw1开始等待之后rw2马上就拿到了锁1,说明等待会释放调用wait的锁资源,但是rw3却没有拿到锁2,而是等rw1释放锁2之后才拿到锁2,这说明wait并不会释放除调用wait的锁之外的锁资源。

PS:
开发成长之旅 [持续更新中...]
关联导航:16:线程基础 - 掘金 (juejin.cn)
关联导航:18:一文看懂JAVA线程池,轻松应对面试
关联导航:19:JAVA线程池常用方法 - 掘金 (juejin.cn)
欢迎关注...