标签:java synchronized 内置锁 对象锁
Java中synchronized关键字和对象的内置锁结合使用,用来保护代码块在并发环境下的线程安全,可以使被保护的代码块操作原子性。
synchronized关键字可以用于修饰方法来保护方法内的全部代码块,可以用synchronized(对象1) 的方式保护指定代码块。(这里说一下:很多书中都说synchronized可以给对象加锁,我实在不愿意这么说,这样让我概念混淆。。。因为,对象内置锁是本来就存在的,不是谁加给它的,“synchronized(对象1)”我更愿意解释成:执行到此的线程需要去获取到(持有)“对象1”的对象锁才能执行synchronized块中的代码)。
而多个线程在执行某一被synchronized保护的代码或者方法时,能够进行互斥的关键在于synchronized的是不是同一个锁。下面是synchronized使用的几种情况并分析他分别持有的是哪一个对象锁(假设这些方法都属于类 Salary):
1、 synchronized修饰非静态方法:持有Salary类实例的对象锁
2、 synchronized修饰静态方法:持有Salary.class的对象锁
3、 synchronized(对象1):持有对象1 的对象锁
4、 synchronized(this):持有Salary类当前对象实例的对象锁
5、 synchronized(Salary.class): 持有Salary.class的对象锁
下面用几个程序实验一下。
一、synchronized修饰非静态方法:持有Salary类实例的对象锁
并发对象类Salary:
public class Salary { public synchronized void method1(){ try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000); } public void method2(){ System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000); } }
并发程序SynchronizeTest:
public class SynchronizeTest { public static void main(String[] args) { final Salary salary = new Salary(); Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000); salary.method1(); System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000); } }); t1.start(); //为了保证线程t1首先获得对象锁,在此处主线程挂起1秒钟,再执行后面的启动线程t2的代码。 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //如果线程t2在启动4秒之后才进入salary.method2(),则说明请求的是同一个对象锁。否则,就不是同一个对象锁 Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000); synchronized (salary) { //TODO1 salary.method2(); } System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000); } }); t2.start(); } }
运行结果:
Thread-0 start at:1452076058
Thread-1 start at:1452076059
Thread-0 has entered method1 at:1452076063
Thread-0 end at:1452076063
Thread-1 has entered method2 at:1452076063
Thread-1 end at:1452076063
从运行结果可以看出,当线程t1进入方法method1 5秒之后(释放了对象锁),线程t2才进入方法method2,说明两个线程持有的是相同的对象锁。
二、synchronized修饰静态方法:持有Salary.class的对象锁
并发对象类Salary
public class Salary { public synchronized static void method1(){ try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000); } public void method2(){ System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000); } }
并发程序SynchronizeTest
public class SynchronizeTest { public static void main(String[] args) { final Salary salary = new Salary(); Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000); Salary.method1(); System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000); } }); t1.start(); //为了保证线程t1首先获得对象锁,在此处主线程挂起1秒钟,再执行后面的启动线程t2的代码。 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //如果线程t2在启动4秒之后才进入salary.method2(),则说明请求的是同一个对象锁。否则,就不是同一个对象锁 Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000); synchronized (Salary.class) { salary.method2(); } System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000); } }); t2.start(); } }
运行结果:
Thread-0 start at:1452076058
Thread-1 start at:1452076059
Thread-0 has entered method1 at:1452076063
Thread-0 end at:1452076063
Thread-1 has entered method2 at:1452076063
Thread-1 end at:1452076063
三、synchronized(对象1):持有对象1 的对象锁
并发对象类Salary:
public class Salary { private Object lock = new Object(); public Salary(Object lock){ this.lock = lock; } public void method1(){ synchronized (lock) { try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000); } } public void method2(){ System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000); } }
并发程序SynchronizeTest:
public class SynchronizeTest { public static void main(String[] args) { final Object 对象1 = new Object(); final Salary salary = new Salary(对象1); Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000); salary.method1(); System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000); } }); t1.start(); //为了保证线程t1首先获得对象锁,在此处主线程挂起1秒钟,再执行后面的启动线程t2的代码。 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //如果线程t2在启动4秒之后才进入salary.method2(),则说明请求的是同一个对象锁。否则,就不是同一个对象锁 Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000); synchronized (对象1) { salary.method2(); } System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000); } }); t2.start(); } }
运行结果:
Thread-0 start at:1452077315
Thread-1 start at:1452077316
Thread-0 has entered method1 at:1452077320
Thread-1 has entered method2 at:1452077320
Thread-1 end at:1452077320
Thread-0 end at:1452077320
四、 synchronized(this):持有Salary类当前对象实例的对象锁
并发对象类Salary:
public class Salary { private Object lock = new Object(); public Salary(Object lock){ this.lock = lock; } public void method1(){ synchronized (this) { try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " has entered method1 at:"+System.currentTimeMillis()/1000); } } public void method2(){ System.out.println(Thread.currentThread().getName() + " has entered method2 at:"+System.currentTimeMillis()/1000); } }
并发程序SynchronizeTest:
public class SynchronizeTest { public static void main(String[] args) { final Object 对象1 = new Object(); final Salary salary = new Salary(对象1); Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000); salary.method1(); System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000); } }); t1.start(); //为了保证线程t1首先获得对象锁,在此处主线程挂起1秒钟,再执行后面的启动线程t2的代码。 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //如果线程t2在启动4秒之后才进入salary.method2(),则说明请求的是同一个对象锁。否则,就不是同一个对象锁 Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+" start at:"+System.currentTimeMillis()/1000); synchronized (salary) { salary.method2(); } System.out.println(Thread.currentThread().getName()+" end at:"+System.currentTimeMillis()/1000); } }); t2.start(); } }
运行结果:
Thread-0 start at:1452077439
Thread-1 start at:1452077440
Thread-0 has entered method1 at:1452077444
Thread-0 end at:1452077444
Thread-1 has entered method2 at:1452077444
Thread-1 end at:1452077444
五、 synchronized(Salary.class): 持有Salary.class的对象锁
与测试程序二同理。
总结一下,synchronized的使用变种可能还有很多,但是万变不离其宗,分析多线程是否在synchronized处产生了互斥,其根本就是要分析synchronized所持有的对象锁是否为同一个对象锁。这很关键!
本文出自 “bccat技术历程” 博客,请务必保留此出处http://bccat.blog.51cto.com/8845284/1732222
标签:java synchronized 内置锁 对象锁
原文地址:http://bccat.blog.51cto.com/8845284/1732222