标签: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