用代码说话:synchronized关键字和多线程访问同步方法的7种情况

synchronized关键字在多线程并发编程中一直是元老级角色的存在,是学习并发编程中必须面对的坎,也是走向Java高级开发的必经之路。

一、synchronized性质

synchronized是Java提供的内置锁机制,有如下两种特性:

互斥性:即在同一时间最多只有一个线程能持有这种锁。当线程1尝试去获取一个由线程2持有的锁时,线程1必须等待或者阻塞,知道线程2释放这个锁。如果线程2永远不释放锁,那么线程1将永远等待下去。

可重入性:即某个线程可以获取一个已经由自己持有的锁。

二、synchronized用法

Java中的每个对象都可以作为锁。根据锁对象的不同,synchronized的用法可以分为以下两种:

对象锁:包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己制定锁对象)

类锁:指的是synchronized修饰静态的方法或指定锁为Class对象。

三、多线程访问同步方法的7种情况

本部分针对面试中常考的7中情况进行代码实战和原理解释。

1. 两个线程同时访问一个对象的同步方法 /** * 两个线程同时访问一个对象的同步方法 */ public class Demo1 implements Runnable { static Demo1 instance = new Demo1(); @Override public void run() { fun(); } public synchronized void fun() { System.out.println(Thread.currentThread().getName() + "开始运行"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public static void main(String[] args) { Thread thread1 = new Thread(instance); Thread thread2 = new Thread(instance); thread1.start(); thread2.start(); while (thread1.isAlive() || thread2.isAlive()) { } System.out.println("finished"); } }

结果:两个线程顺序执行。

两个线程同时访问一个对象的同步方法

解释:thread1和thread2共用一把锁instance;同一时刻只能有一个线程获取锁;thread1先启动,先获得到锁,先运行,此时thread2只能等待。当thread1释放锁之后,thread2获取到锁,进行执行。

2. 两个线程访问的是两个对象的同步方法 public class Demo2 implements Runnable{ static Demo2 instance1 = new Demo2(); static Demo2 instance2 = new Demo2(); @Override public void run() { fun(); } public synchronized void fun() { System.out.println(Thread.currentThread().getName() + "开始运行"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public static void main(String[] args) { Thread thread1 = new Thread(instance1); Thread thread2 = new Thread(instance2); thread1.start(); thread2.start(); while (thread1.isAlive() || thread2.isAlive()) { } System.out.println("finished"); } }

结果: 两个线程并行执行。

两个线程访问的是两个对象的同步方法

解释:thread1使用的锁对象是instance1,thread2使用的锁对象是instance2,两个对象使用的锁对象不是同一个,所以线程之间互不影响,是并行执行的。

3. 两个线程访问的是synchronized的静态方法 public class Demo3 implements Runnable{ static Demo3 instance1 = new Demo3(); static Demo3 instance2 = new Demo3(); @Override public void run() { fun(); } public static synchronized void fun() { System.out.println(Thread.currentThread().getName() + "开始运行"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public static void main(String[] args) { Thread thread1 = new Thread(instance1); Thread thread2 = new Thread(instance2); thread1.start(); thread2.start(); while (thread1.isAlive() || thread2.isAlive()) { } System.out.println("finished"); } }

结果:两个线程顺序执行。

两个线程访问的是synchronized的静态方法

解释:虽然两个线程使用了两个不同的instance实例,但是只要方法是静态的,对应的锁对象是同一把锁,需要先后获取到锁进行执行。

4. 同时访问同步方法与非同步方法 public class Demo4 implements Runnable { static Demo4 instance = new Demo4(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ fun1(); }else{ fun2(); } } public synchronized void fun1() { System.out.println(Thread.currentThread().getName() + "开始运行"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "fun1运行结束"); } public void fun2() { System.out.println(Thread.currentThread().getName() + "fun2开始运行"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行结束"); } public static void main(String[] args) { Thread thread1 = new Thread(instance); Thread thread2 = new Thread(instance); thread1.start(); thread2.start(); while (thread1.isAlive() || thread2.isAlive()) { } System.out.println("finished"); } }

结果:两个线程并行执行。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwxydx.html