Java synchronized解析
多线程三大特性:
可见性、原子性、有序性
synchronize的特性:
1、同一时刻只有一个线程访问临界资源
2、其它未获取到锁执行权的线程必须排队等待
3、保证共享资源的原子性、可见性和有序性
4、进入synchronized范围内自动加锁,synchronized作用域外锁自动消除,即使异常也会释放锁
synchronize加锁的方式:
-
对于普通同步方法,锁是当前实例对象。
-
对于静态同步方法,锁是当前类的Class对象。
-
对于同步方法块,锁是Synchonized括号里配置的对象。
通过具体的例子来看一下
首先是普通方法:
class NoSyncTest { public void method1() { Log.i("sync", "method 1 start"); try { Log.i("sync", "method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.i("sync", "method 1 end"); } public void method2() { Log.i("sync", "method 2 start"); try { Log.i("sync", "method 2 execute"); Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.i("sync", "method 2 end"); } } private void noSyncTest() { final NoSyncTest test = new NoSyncTest(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { test.method1(); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { test.method2(); } }); thread1.start(); thread2.start(); }
这是一个没有任何同步的方法,NoSyncTest这个类有两个方法method1和method2,分别执行睡3s和0.5s的动作,然后再两个线程中分别调用这个类的实例test的两个方法,看一下结果:
可以看到method2和method1同时执行,method2因为sleep的时间短所以先结束。
再看一下普通方法同步:
class MethodSyncTest { public synchronized void method1() { Log.i("sync", "method 1 start"); try { Log.i("sync", "method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.i("sync", "method 1 end"); } public synchronized void method2() { Log.i("sync", "method 2 start"); try { Log.i("sync", "method 2 execute"); Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.i("sync", "method 2 end"); } } private void MethodSyncTest() { final MethodSyncTest test = new MethodSyncTest(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { test.method1(); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { test.method2(); } }); thread1.start(); thread2.start();
synchronize修饰的method1和method2,其他不变,看一下结果:
method1先执行然后3s之后结束了method2才开始执行。(注意这个地方不能new 不同的对象来调用方法,因为修饰普通方法本质是对对象的同步加锁。)
看一下第三种静态同步方法:
static class StaticMethodSyncTest { public static synchronized void method1() { Log.i("sync", "method 1 start"); try { Log.i("sync", "method 1 execute"); Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.i("sync", "method 1 end"); } public static synchronized void method2() { Log.i("sync", "method 2 start"); try { Log.i("sync", "method 2 execute"); Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.i("sync", "method 2 end"); } public static synchronized void method3() { Log.i("sync", "method 3 start"); try { Log.i("sync", "method 3 execute"); Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.i("sync", "method 3 end"); } } private void StaticMethodSyncTest() { final StaticMethodSyncTest test1 = new StaticMethodSyncTest(); final StaticMethodSyncTest test2 = new StaticMethodSyncTest(); Thread thread1 = new Thread(new Runnable() { @Override public void run() { test1.method1(); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { test2.method2(); } }); Thread thread3 = new Thread(new Runnable() { @Override public void run() { StaticMethodSyncTest.method3(); } }); thread1.start(); thread2.start(); thread3.start(); }
static修饰方法相当于这个方法是类方法,可以直接通过类名.方法名调用。我们在这new出了test1和test2两个对象分别调用method1和method2,以及通过类名.方法名调用method3,看一下结果
method1、method2、method3顺序执行。(同步静态方法的本质是锁的当前类)