8锁现象
问题一
-
在标准情况下,两个线程先打印 发短信 还是 打电话 ?
package com.zxh.lock8; import java.util.concurrent.TimeUnit; public class Test1 { public static void main(String[] args) { Phone phone = new Phone(); // 线程A new Thread(()->{ phone.seedMsg(); }, "A").start(); // 1秒延迟 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 线程B new Thread(()->{ phone.call(); }, "B").start(); } } class Phone{ public synchronized void seedMsg(){ System.out.println("发短信"); } public synchronized void call(){ System.out.println("打电话"); } }
为什么?
问题二
package com.zxh.lock8; import java.util.concurrent.TimeUnit; public class Test1 { public static void main(String[] args) { Phone phone = new Phone(); // 线程A,人 new Thread(()->{ phone.seedMsg(); }, "A").start(); // 1秒延迟 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 线程B,人 new Thread(()->{ phone.call(); }, "B").start(); } } // 手机 class Phone{ public synchronized void seedMsg(){ // 4秒延迟 try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public synchronized void call(){ System.out.println("打电话"); } }
答案还是发短信。
为什么?
-
首先知道 锁的对象是谁?因为 synchronized 加在方法上,所以锁的对象是 方法的调用者
-
所以两个方法用的是同一个锁,谁先拿到谁先执行!
通俗解释:
-
phone对象,就是方法的调用者,也就是手机,它可以打电话和发短信。
-
现在有两个人线程A 和 线程B,他们一个想打电话,一个想发短信。
-
线程A,先拿到锁(也就是手机),抱着锁(手机)睡了4秒。
-
线程B肯定拿不到锁(手机),需要等待。
问题三
-
Phone类增加一个普通方法,线程B调用,那么两个线程先打印 发短信 还是 打电话?
package com.zxh.lock8; import java.util.concurrent.TimeUnit; public class Test2 { public static void main(String[] args) { Phone2 phone = new Phone2(); // 线程A,人 new Thread(()->{ phone.seedMsg(); }, "A").start(); // 1秒延迟 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 线程B,人 new Thread(()->{ phone.hello(); }, "B").start(); } } // 手机 class Phone2{ //同步方法 public synchronized void seedMsg(){ // 4秒延迟 try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } //同步方法 public synchronized void call(){ System.out.println("打电话"); } //普通方法 public void hello(){ System.out.println("hello"); } }
为什么?
因为hello()
方法,是普通方法,根本不受锁的影响。
通俗解释:
-
锁的是方法的调用者(手机)
-
问题四
-
创建两个phone对象,线程调用不同对象的方法,那么两个线程先打印 发短信 还是 打电话?
package com.zxh.lock8; import java.util.concurrent.TimeUnit; public class Test2 { public static void main(String[] args) { Phone2 phone1 = new Phone2(); Phone2 phone2 = new Phone2(); // 线程A,人 new Thread(()->{ phone1.seedMsg(); }, "A").start(); // 1秒延迟 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 线程B,人 new Thread(()->{ phone2.call(); }, "B").start(); } } // 手机 class Phone2{ //同步方法 public synchronized void seedMsg(){ // 4秒延迟 try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } //同步方法 public synchronized void call(){ System.out.println("打电话"); } //普通方法 public void hello(){ System.out.println("hello"); } }
答案是打电话。
为什么?
最重要的一点,synchronized用在方法上,那么锁的是方法的调用者。现在有两个调用者,互不影响。
-
有两个手机,两个人抱着各自的手机的锁,当然互不影响了。
锁class模板
问题五
-
将方法变为静态同步方法,那么两个线程先打印 发短信 还是 打电话?
package com.zxh.lock8; import java.util.concurrent.TimeUnit; public class Test3 { public static void main(String[] args) { Phone3 phone = new Phone3(); // 线程A,人 new Thread(()->{ phone.seedMsg(); }, "A").start(); // 1秒延迟 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 线程B,人 new Thread(()->{ phone.call(); }, "B").start(); } } // 手机 class Phone3{ // 静态同步方法 public static synchronized void seedMsg(){ // 4秒延迟 try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } // 静态同步方法 public static synchronized void call(){ System.out.println("打电话"); } }
为什么?
那只是回答了一半,static静态方法,在程序刚执行的时候,就加载到内存中生成了.class模板
这个class模板是唯一的,所以锁静态方法,其实锁的就是class模板。
问题六
-
现在有两个对象,调用不同对象的,那么两个线程先打印 发短信 还是 打电话?
package com.zxh.lock8; import java.util.concurrent.TimeUnit; public class Test3 { public static void main(String[] args) { Phone3 phone1 = new Phone3(); Phone3 phone2 = new Phone3(); // 线程A,人 new Thread(()->{ phone1.seedMsg(); }, "A").start(); // 1秒延迟 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 线程B,人 new Thread(()->{ phone2.call(); }, "B").start(); } } // 手机 class Phone3{ // 静态同步方法 public static synchronized void seedMsg(){ // 4秒延迟 try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } // 静态同步方法 public static synchronized void call(){ System.out.println("打电话"); } }
答案是发短信。
为什么?
因为锁的是class模板,而这个模板是唯一的一个,所以无论几个对象,使用的锁都是一样。
锁方法的调用者和class模板
问题七
-
资源类中,一个是静态同步方法,一个普通同步方法,那么两个线程先打印 发短信 还是 打电话?
package com.zxh.lock8; import java.util.concurrent.TimeUnit; public class Test4 { public static void main(String[] args) { Phone4 phone1 = new Phone4(); // 线程A,人 new Thread(()->{ phone1.seedMsg(); }, "A").start(); // 1秒延迟 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 线程B,人 new Thread(()->{ phone1.call(); }, "B").start(); } } // 手机 class Phone4{ // 静态同步方法 public static synchronized void seedMsg(){ // 4秒延迟 try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } // 普通同步方法 public synchronized void call(){ System.out.println("打电话"); } }
答案是打电话。
为什么?
两个锁的东西是不同,一个锁的是class模板,一个锁的是方法调用者,两个都是互不影响。
可能有人会不确定,锁了class模板,虽然调用的是不同方法,但是可以调用吗?
是可以的,创建的phone对象是共享class资源的。
问题八
-
在上面的基础上,增加一个对象,两个对象分别调用静态和非静态的同步方法,那么两个线程先打印 发短信 还是 打电话?
package com.zxh.lock8; import java.util.concurrent.TimeUnit; public class Test4 { public static void main(String[] args) { Phone4 phone1 = new Phone4(); Phone4 phone2 = new Phone4(); // 线程A,人 new Thread(()->{ phone1.seedMsg(); }, "A").start(); // 1秒延迟 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 线程B,人 new Thread(()->{ phone2.call(); }, "B").start(); } } // 手机 class Phone4{ // 静态同步方法 public static synchronized void seedMsg(){ // 4秒延迟 try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } // 普通同步方法 public synchronized void call(){ System.out.println("打电话"); } }
答案是打电话。
为什么?
不要怀疑,以为增加了一个对象,就会有什么影响?
不会的,只要关注锁的是什么,就不会有什么问题的。
打电话和发短信的方法,锁的是不同的对象。不会有影响。
8锁问题小结
new 和 this 是一个对象。
static 和 Class 是唯一的一个模板。