美文网首页
简单聊聊sleep&wait& synchronized

简单聊聊sleep&wait& synchronized

作者: tangxqa | 来源:发表于2019-07-28 17:51 被阅读0次

    一、sleep & wait

    首先从一个面试问题开篇:我们经常用的sleep() & wait() 方法有什么不同?
    基于我自己的知识范围,我给出以下回答:

    1. sleep方法是Thread类的静态方法,而wait方法是Object类下的非静态方法
    2. sleep方法 执行时不会释放monitor;而wait方法在执行时会释放monitor,并将当前线程加入到等待monitor的wait set中。
    3. 啥时候想让当前线程休眠了,直接Thread.sleep(x)就好,不需要依赖monitor;但wait需要
    4. 通常情况下,sleep方法的执行线程不需要别人唤醒,但wait()通常需要别人notify()后才能唤醒接着执行,当然wait(x)方法除外。

    对于第二点,通过以下代码进行验证。
    关于sleep,我们常规的sleep写法:

    public static void main(String[] args) {
            try {
                Thread.sleep(30000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    

    此时通过jstack查看线程状态发现:[main线程并没有在等待monitor]

    "main" #1 prio=5 os_prio=31 tid=0x00007fe24d002000 nid=0x1903 waiting on condition [0x0000700006f50000]
       java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
    

    关于wait,我们通过以下代码来对wait方法进行说明:

    public static void main(String[] args) {
    
            Stream.of("T1", "T2").forEach(name ->
                    new Thread(name) {
                        @Override
                        public void run() {
    //                        m1();
                        m2();
                        }
                    }.start()
            );
        }
    
        public static void m1() {
            synchronized (LOCK) {
                try {
                    System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
    
        public static void m2() {
            synchronized (LOCK) {
                try {
                    System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    

    首先注掉m1(),放开m2()。运行程序,得到运行结果:

    The Thread T1 enter.
    The Thread T2 enter.

    这就说明,在T1获得LOCK进入synchronized代码块之后,通过wait将锁进行了释放,使得T2尝试获取LOCK时能够成功获取,并进入synchronzied代码块执行了println语句。

    那如果注掉m2()放开m1()呢?结果如下:

    The Thread T1 enter.

    通过jstack发现,由于T1手里握着锁,仍处在synchronized代码块中,因此T2在尝试获取锁时因为无法获取而进入阻塞状态:

    "T2" #14 prio=5 os_prio=31 tid=0x00007fd96902a000 nid=0x5b03 waiting for monitor entry [0x000070000be19000]
       java.lang.Thread.State: BLOCKED (on object monitor)
    
    "T1" #13 prio=5 os_prio=31 tid=0x00007fd968895000 nid=0xa903 waiting on condition [0x000070000bd16000]
       java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
    

    关于第三点,做以下测试,即去掉获取锁的代码为:

        public static void m2() {
    //        synchronized (LOCK) {
                try {
                    System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
                    LOCK.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    //        }
        }
    

    执行结果:

    Exception in thread "T1" Exception in thread "T2" java.lang.IllegalMonitorStateException
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Object.java:502)
    

    所以结论为:需要先获取锁才能执行wait(包括notify、notifyAll等)操作

    二、synchronized

    通过以上代码我们注意到,synchronized使用的是一个实例化之后的Object类的对象LOCK作为锁,那就聊聊关于synchronized的几种写法,以及这几种写法分别把锁加到了哪里。
    同步代码块

    final Object LOCK = new Object();
    
     synchronized (LOCK) {
                try {
                    System.out.println("The Thread " + Thread.currentThread().getName() + " enter.");
                    Thread.sleep(20000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    

    这种写法就是很明显的用LOCK对象作为锁用于线程同步

    同步方法
    被synchronized所修饰的方法的所在类:

    public class SychronizedStatic {
        public synchronized  void m4() {
            System.out.println("m4 " + Thread.currentThread().getName());
            try {
                Thread.sleep(10_000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public synchronized  void m5() {
            System.out.println("m5 " + Thread.currentThread().getName());
            try {
                Thread.sleep(10_000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    测试代码:

    public static void main(String[] args) {
            SychronizedStatic sychronizedStatic = new SychronizedStatic();
            new Thread("T4") {
                @Override
                public void run() {
                    sychronizedStatic.m4();
                }
            }.start();
    
            new Thread("T5") {
                @Override
                public void run() {
                    sychronizedStatic.m5();
                }
            }.start();
    }
    
    

    这种写法也比较好理解,作为锁的对象就是调用者,即sychronizedStatic

    静态同步方法

    被synchronized所修饰的静态方法的所在类:

    public class SychronizedStatic {
        public synchronized static void m1() {
            System.out.println("m1 " + Thread.currentThread().getName());
            try {
                Thread.sleep(10_000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public synchronized static void m2() {
            System.out.println("m2 " + Thread.currentThread().getName());
            try {
                Thread.sleep(10_000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void m3() {
            synchronized (SychronizedStatic.class) {
                System.out.println("m3 " + Thread.currentThread().getName());
                try {
                    Thread.sleep(10_000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
    }
    

    测试方法:

        public static void main(String[] args) {
            new Thread("T1") {
                @Override
                public void run() {
                    SychronizedStatic.m1();
                }
            }.start();
    
            new Thread("T2") {
                @Override
                public void run() {
                    SychronizedStatic.m2();
                }
            }.start();
    
    }
    

    测试结果如下:

    m1 T1

    这就说明,m2()m3()阻塞在了SychronizedStatic.class上,也就是说被synchronized修饰的静态方法,在做线程同步时,加锁是加在静态方法所在的静态类Class对象上的。

    三、 瞎扯

    sleep & wait & synchronize,这都是java开发最基本的东西,但是如果稍微细问问的话,还真不一定理解的那么透彻。因此,学无止境,要融会贯通~

    相关文章

      网友评论

          本文标题:简单聊聊sleep&wait& synchronized

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