Synchronized是Java中解决并发问题的一种最经常使用的方法,也是最简单的一种方法。Synchronized的做用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改可以及时可见(3)有效解决重排序问题。从语法上讲,Synchronized总共有三种用法:html
1)、没有同步的状况:java
/** * 没有同步的状况 * * Created by Jiacheng on 2018/6/28. */ public class SynchronizedTest { public void method1() { System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public void method2() { System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedTest test = new SynchronizedTest(); new Thread(() -> test.method1()).start(); new Thread(() -> test.method2()).start(); } }
2)、对普通方法同步:编程
/** * 同步实例方法 * * Created by Jiacheng on 2018/6/28. */ public class SynchronizedMethod { public synchronized void method1() { System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public synchronized void method2() { System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedMethod test = new SynchronizedMethod(); new Thread(() -> test.method1()).start(); new Thread(() -> test.method2()).start(); } }
虽然method1和method2是不一样的方法,可是这两个方法都进行了同步,而且是经过同一个对象去调用的,因此调用以前都须要先去竞争同一个对象上的锁(monitor),也就只能互斥的获取到锁,所以,method1和method2只能顺序的执行。多线程
3)、静态方法(类)同步:并发
/** * 同步静态方法 * * Created by Jiacheng on 2018/6/28. */ public class SynchronizedStatic { public static synchronized void method1() { System.out.println("Method 1 start"); try { System.out.println("Method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public static synchronized void method2() { System.out.println("Method 2 start"); try { System.out.println("Method 2 execute"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedStatic test = new SynchronizedStatic(); final SynchronizedStatic test2 = new SynchronizedStatic(); new Thread(() -> test.method1()).start(); new Thread(() -> test2.method2()).start(); } }
虽然test和test2属于不一样对象,可是test和test2属于同一个类的不一样实例,因为method1和method2都属于静态同步方法,对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法),因此调用的时候须要获取同一个类上monitor(每一个类只对应一个class对象),因此也只能顺序的执行。函数
4)、代码块同步:高并发
/** * 同步方法块 * * Created by Jiacheng on 2018/6/28. */ public class SynchronizedBlock { public void method1() { System.out.println("Method 1 start"); try { synchronized (this) { System.out.println("Method 1 execute"); Thread.sleep(3000); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 1 end"); } public void method2() { System.out.println("Method 2 start"); try { synchronized (this) { System.out.println("Method 2 execute"); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Method 2 end"); } public static void main(String[] args) { final SynchronizedBlock test = new SynchronizedBlock(); new Thread(() -> test.method1()).start(); new Thread(() -> test.method2()).start(); } }
对于代码块的同步实质上须要获取Synchronized关键字后面括号中对象的monitor,因为这段代码中括号的内容都是this,而method1和method2又是经过同一的对象去调用的,因此进入同步块以前须要去竞争同一个对象上的锁,所以只能顺序执行同步块。性能
每一个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的全部权,过程以下:this
1)若是monitor的进入数为0,则该线程进入monitor,而后将进入数设置为1,该线程即为monitor的全部者。spa
2)若是线程已经占有该monitor,只是从新进入,则进入monitor的进入数加1.
3)若是其余线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再从新尝试获取monitor的全部权。
synchronized 方法控制对类成员变量的访问:每一个类实例对应一把锁,每一个 synchronized 方法都必须得到调用该方法的类实例的锁方能执行,不然所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能得到该锁,从新进入可执行状态。这种机制确保了同一时刻对于每个类实例,其全部声明为 synchronized 的成员函数中至多只有一个处于可执行状态(由于至多只有一个可以得到该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要全部可能访问类成员变量的方法均被声明为 synchronized)。
在 Java 中,不光是类实例,每个类也对应一把锁,这样咱们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。synchronized方法实际上等同于用一个synchronized块包住方法中的全部语句,而后在synchronized块的括号中传入this关键字。固然若是是静态方法,须要锁定的则是class对象。
可能一个方法中只有几行代码涉及到线程同步的问题,因此synchronized块比synchronized方法更近细粒度的控制了多个线程的访问,只有synchronized块中的内容不能同时被多个线程访问,方法中的其余语句仍然能够同时被多个线程所访问(包括synchronized块以前和以后的)。
使用synchronized,当多个线程尝试获取锁时,未获取到锁的线程会不断的尝试获取锁,而不会发生中断,这样会形成性能消耗。
参考资料
《Java多线程编程实战指南》