synchronize能够在多个线程操做同一个成员变量或者方法时,实现同步(或者互斥)的效果。
synchronized能够做用于方法,以及方法内部的代码块。异步
//1 synchronized void method(){} //2 static synchronized void method(){} //3 synchronized void method(){ synchronized(锁对象){ } } //4 static synchronized void method(){ synchronized(锁对象){ } }
那么在上面的示例中,它们分别持有的锁对象是谁?
synchronized做用于非静态方法以及非静态方法内部的代码块,持有的是当前类的对象的锁,而且是同一个锁。做用于静态方法及其内部的代码块,持有的是当前类的Class对象的锁,而且和非静态方法不是同一个锁。
经过代码来验证。ide
public class SynchronizedTest { private synchronized void test1(){ for (int x = 0; x < 5; x++) { System.out.println("test1---"+x); } } private void test2(){ synchronized(this) { for (int x = 0; x < 5; x++) { System.out.println("---test2---"+x); } } } private static synchronized void test3(){ for (int x = 0; x < 5; x++) { System.out.println("------test3---"+x); } } private static void test4(){ synchronized (SynchronizedTest.class){ for (int x = 0; x < 5; x++) { System.out.println("---------test4---"+x); } } } public static void main(String[] args) { SynchronizedTest synchronizedTest = new SynchronizedTest(); new Thread(new Runnable() { @Override public void run() { synchronizedTest.test1(); } }).start(); new Thread(new Runnable() { @Override public void run() { synchronizedTest.test2(); } }).start(); new Thread(new Runnable() { @Override public void run() { test3(); } }).start(); new Thread(new Runnable() { @Override public void run() { test4(); } }).start(); } }
执行结果this
test1---0 ------test3---0 test1---1 ------test3---1 test1---2 test1---3 ------test3---2 test1---4 ------test3---3 ------test3---4 ---test2---0 ---test2---1 ---test2---2 ---test2---3 ---test2---4 ---------test4---0 ---------test4---1 ---------test4---2 ---------test4---3 ---------test4---4
test1和test2不会交叉执行,test3和test4也不会交叉执行。非静态方法以及方法内部的代码块持有的是同一个对象锁,它们是同步执行的。静态方法和内部的代码块持有的是当前类的Class对象锁,它们是同步执行的。而静态方法和非静态方法持有的不是同一个锁,它们是异步的。线程
字符串常量做为锁,会有什么结果?code
final String a = "100"; final String b = "100"; new Thread(new Runnable() { @Override public void run() { synchronized (a){ while (true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } } },"thread-a").start(); new Thread(new Runnable() { @Override public void run() { synchronized (b){ while (true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } } },"thread-b").start();
这里字符串a和b虽然是两个对象,可是声明b时,会将字符串常量池中已存在的a的值直接赋给b。这样a和b实际上是同样的。这样线程thread-a和thread-b同时抢占同一个锁,一旦一个线程抢到该锁,另外一个线程就再也获取不到该锁。对象
本身覆盖了父类被synchronized修饰的方法,子类方法若是须要同步性,也须要用synchronized修饰。
定义子类Sub继承自SynchronizedTest继承
class Sub extends SynchronizedTest{ @Override public void test2() { //super.test2(); for (int x = 0; x < 15; x++) { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } }
而后开启两个线程调用Sub的test2()方法。字符串
final Sub sub = new Sub(); new Thread(new Runnable() { @Override public void run() { sub.test2(); } },"Sub---A").start(); new Thread(new Runnable() { @Override public void run() { sub.test2(); } },"Sub---B").start();
打印结果get
Sub---A Sub---B Sub---A Sub---B Sub---A Sub---B Sub---A Sub---B
可见Sub的test2()方法并无同步性。也就是synchronized不能被继承。同步
使用synchronized时,当一个线程请求一个对象锁时,再次请求该锁是能够当即获得的。
public class SynchronizedTest { private synchronized void test1(){ for (int x = 0; x < 5; x++) { System.out.println("test1---"+x); } test2(); } public void test2(){ synchronized(this) { for (int x = 0; x < 5; x++) { System.out.println("---test2---"+x); } } } public static void main(String[] args) { SynchronizedTest synchronizedTest = new SynchronizedTest(); new Thread(new Runnable() { @Override public void run() { synchronizedTest.test1(); } }).start(); } }
在方法test1()中调用方法test2(),并不须要等待锁,而是当即获取到锁。
把子类Sub的test2()方法也改为synchronized修饰。并在其内部调用父类的test2()方法。能得到锁吗?
class Sub extends SynchronizedTest{ @Override public synchronized void test2() { //调用父类的同步方法 super.test2(); for (int x = 0; x < 15; x++) { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } } }
打印结果
---test2---0 ---test2---1 ---test2---2 ---test2---3 ---test2---4 Sub---A Sub---A Sub---A Sub---A
能够看到父类方法执行完毕,子类的方法当即执行。可见,在子父类的继承关系中,也支持可重入锁这个特性。
synchronized做用于整个方法,可能引发方法执行效率降低。建议将方法内部须要同步的代码用synchronized修饰,也就是synchronized代码块。
在一个类中假若有多个同步方法,它们之间并不须要互斥。那么使用同一个锁,会大大下降效率。能够定义多个同步锁对象。
Object obj1 = new Object(); Object obj2 = new Object(); public void method1(){ synchronized(obj1){ } } public void method2(){ synchronized(obj2){ } }