前言
多线程是Java面试中最喜欢问的问题之一,有一篇公众号推文内容很详细,推荐看看
本文中仅对synchronized关键字的加锁进行一定分析
一、标准情况访问
按照普通的情况访问同步方法,查看输出
1 class Phone { 2 public synchronized void getIOS() throws Exception { 3 System.out.println("---getIOS"); 4 } 5 public synchronized void getAndroid() throws Exception { 6 System.out.println("---getAndroid"); 7 } 8 } 9 10 public class ThreadDemo { 11 12 public static void main(String[] args) { 13 final Phone phone = new Phone(); 14 new Thread(new Runnable() { 15 16 @Override 17 public void run() { 18 for (int i = 0; i < 1; i++) { 19 try { 20 phone.getIOS(); 21 } catch (Exception e) { 22 e.printStackTrace(); 23 } 24 } 25 } 26 }, "AA").start(); 27 new Thread(new Runnable() { 28 29 @Override 30 public void run() { 31 for (int i = 0; i < 1; i++) { 32 try { 33 phone.getAndroid(); 34 } catch (Exception e) { 35 e.printStackTrace(); 36 } 37 } 38 } 39 }, "BB").start(); 40 } 41 42 }
执行结果:
---getIOS
---getAndroid
二、在其中一种方法中添加sleep方法访问
如下代码,在getIOS方法中添加sleep,查看修改后的打印顺序
class Phone { public synchronized void getIOS() throws Exception { Thread.sleep(3000); System.out.println("---getIOS"); } public synchronized void getAndroid() throws Exception { System.out.println("---getAndroid"); } }
执行结果:(两个结果在3秒后同时出现)
---getIOS
---getAndroid
三、添加一个未加锁方法,查看访问结果
在Phone类中添加getHello方法,同时线程修改为访问同步方法和普通方法
class Phone { public synchronized void getIOS() throws Exception { Thread.sleep(3000); System.out.println("---getIOS"); } public synchronized void getAndroid() throws Exception { System.out.println("---getAndroid"); } public void getHello() { System.out.println("---getHello"); } } public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { phone.getIOS(); } catch (Exception e) { e.printStackTrace(); } } } }, "AA").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { //phone.getAndroid(); phone.getHello(); } catch (Exception e) { e.printStackTrace(); } } } }, "BB").start(); } }
执行结果:
---getHello(直接出现)
---getIOS(三秒后出现)
四、执行时创建两个不同对象,通过不同对象访问加锁的方法
在创建线程时,通过不同对象执行同步方法,查看执行结果
class Phone { public synchronized void getIOS() throws Exception { Thread.sleep(3000); System.out.println("---getIOS"); } public synchronized void getAndroid() throws Exception { System.out.println("---getAndroid"); } public void getHello() { System.out.println("---getHello"); } } public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone(); final Phone phone2 = new Phone(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { phone.getIOS(); } catch (Exception e) { e.printStackTrace(); } } } }, "AA").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { //phone.getAndroid(); //phone.getHello(); phone2.getAndroid(); } catch (Exception e) { e.printStackTrace(); } } } }, "BB").start(); } }
执行结果:
---getAndroid(直接出现)
---getIOS(三秒后出现)
五、将加锁的方法改为静态方法,同一个对象执行
两个同步方法都改为静态方法,通过同一个对象执行方法,查看执行结果
class Phone { public static synchronized void getIOS() throws Exception { Thread.sleep(3000); System.out.println("---getIOS"); } public static synchronized void getAndroid() throws Exception { System.out.println("---getAndroid"); } public void getHello() { System.out.println("---getHello"); } } public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone(); //final Phone phone2 = new Phone(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { phone.getIOS(); } catch (Exception e) { e.printStackTrace(); } } } }, "AA").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { phone.getAndroid(); //phone.getHello(); //phone2.getAndroid(); } catch (Exception e) { e.printStackTrace(); } } } }, "BB").start(); } }
执行结果:(两个结果在三秒后同时出现)
---getIOS
---getAndroid
六、通过两个对象访问静态同步方法
将上一个的执行对象改为phone2,查看执行结果
class Phone { public static synchronized void getIOS() throws Exception { Thread.sleep(3000); System.out.println("---getIOS"); } public static synchronized void getAndroid() throws Exception { System.out.println("---getAndroid"); } public void getHello() { System.out.println("---getHello"); } } public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone(); final Phone phone2 = new Phone(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { phone.getIOS(); } catch (Exception e) { e.printStackTrace(); } } } }, "AA").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { //phone.getAndroid(); //phone.getHello(); phone2.getAndroid(); } catch (Exception e) { e.printStackTrace(); } } } }, "BB").start(); } }
执行结果:(两个结果在三秒后同时出现)
---getIOS
---getAndroid
七、一个静态同步方法修改为非静态,通过同一个对象执行
其中的一个方法去掉静态关键字,查看执行结果
class Phone { public static synchronized void getIOS() throws Exception { Thread.sleep(3000); System.out.println("---getIOS"); } public synchronized void getAndroid() throws Exception { System.out.println("---getAndroid"); } public void getHello() { System.out.println("---getHello"); } } public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone(); final Phone phone2 = new Phone(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { phone.getIOS(); } catch (Exception e) { e.printStackTrace(); } } } }, "AA").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { phone.getAndroid(); //phone.getHello(); //phone2.getAndroid(); } catch (Exception e) { e.printStackTrace(); } } } }, "BB").start(); } }
执行结果:
---getAndroid(直接出现)
---getIOS(三秒后出现)
八、一个静态同步方法修改为非静态,通过两个对象执行
其中的一个方法去掉静态关键字,查看执行结果
class Phone { public static synchronized void getIOS() throws Exception { Thread.sleep(3000); System.out.println("---getIOS"); } public synchronized void getAndroid() throws Exception { System.out.println("---getAndroid"); } public void getHello() { System.out.println("---getHello"); } } public class ThreadDemo { public static void main(String[] args) { final Phone phone = new Phone(); final Phone phone2 = new Phone(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { phone.getIOS(); } catch (Exception e) { e.printStackTrace(); } } } }, "AA").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1; i++) { try { //phone.getAndroid(); //phone.getHello(); phone2.getAndroid(); } catch (Exception e) { e.printStackTrace(); } } } }, "BB").start(); } }
执行结果:
---getAndroid(直接出现)
---getIOS(三秒后出现)
总结
1. 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
2. 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
3. 加个普通方法后发现和同步锁无关
4. 换成两个对象后,不是同一把锁了,情况立刻变化。
5. 都换成静态同步方法后,情况又变化
所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!