我对synchronized关键字算是很熟悉的了,但是有些时候一些概念还是没有搞明白,今天通过对wiki翻译以及博客的学习,从概念和类别上对其进行区分,并整理了一套自己的理解方式。
从类别来区分
从类别上可以将锁分为类级别锁和对象级别锁
1. 类级别锁,作用于整个类的的同步锁,例如Object.class
2. 对象级别锁,new Object()产生的锁,不同的实例所持有的锁是不同的。
3. this锁,也属于对象级别的锁。不同的实例所持有的锁是不同的。
类级别锁和对象级别锁的区别
- 类级别锁是针对类的,无论创建多少实例对象,他们都拥有同一个类级别的锁。
- 对象级别锁,针对实例对象。伴随着new对象,会有不同的锁对象。不同的锁没有互斥性。
下面看一段代码:
使用代码块来说明两者的区别,更加直观一些
synchronized (blockerLock){}代表对象级别的锁,
synchronized (SyncThread.class){}代表类级别锁
static class SyncThread extends Thread{
//这里写成非静态的,随着SyncThread的创建,同时生成不同的blockerLock对象
private Object blockerLock = new Object();
public SyncThread(){
}
@Override
public void run() {
synchronized (blockerLock){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 当前等待的线程object " + blockerLock);
synchronized (SyncThread.class){
System.out.println(Thread.currentThread().getName() + " 当前等待的线程 " + SyncThread.class);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 当前锁 SyncThread.class= " + SyncThread.class);
}
System.out.println(Thread.currentThread().getName() + "over = ");
}
}
}
代码调用部分分析
随着new SyncThread()的创建,同时也创建多个blockerLock对象,在synchronized (blockerLock)时,对象锁不同,因此不会互斥。打印的对象值是随机的
而进入synchronized (SyncThread.class)时,都持有类锁,所以会进入互斥状态,每个线程会等待锁的释放。所以打印的对象是一致的。
public static void main(String[] args) {
syncThreadTest();
}
public static void syncThreadTest(){
for (int i=0; i < 10; i++){
SyncThread syncThread = new SyncThread();
syncThread.start();
}
}
synchronized的修饰范围
- 修饰方法
相当于对象级别锁(this)
public synchronized void method(String name,String description)
- 修饰静态方法
相当于类级别锁
public synchronized static void staticMethod(String name,String description)
- 同步代码块
可以根据需求添加不同的对象级别锁或者类级别锁
public void methodToBlock(){
synchronized (SyncObject.class){
System.out.println("synchronized Block method SyncObject.class");
synchronized (this){
System.out.println("synchronized Block method this");
}
}
System.out.println("非锁代码块的输出...");
}
死锁
- 一开始,线程A持有锁Lock1,线程B持有锁Lock2。
- 当线程A去执行toWc()时,需要持有锁Lock2,并释放Lock1,但是Lock2的锁正在被线程B所持有,因此线程A在等待状态;
- 线程B也去执行toWc()时,需要持有Lock1,并释放Lock2,但是Lock1被线程A所持有,因此线程B也处于等待状态。
public class DeadLock {
private String did;
public DeadLock(String did){
this.did = did;
}
public synchronized void eat(DeadLock deadLock){
System.out.println(this.toString() + " 当前对象 " + Thread.currentThread().getName() + " 排队吃饭");
try {
Thread.sleep(1000);
System.out.println(this.toString() + " 当前对象 " + Thread.currentThread().getName() + " 模拟吃饭过程....");
deadLock.toWc();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void toWc(){
System.out.println(this.toString() + " 当前对象 " + Thread.currentThread().getName() + " 排队上厕所");
try {
Thread.sleep(1000);
System.out.println(this.toString() + " 当前对象 " + Thread.currentThread().getName() + " 模拟上厕所过程....");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return did;
}
public static void main(String[] args) {
final DeadLock deadLock1 = new DeadLock("LockA");
final DeadLock deadLock2 = new DeadLock("LockB");
Runnable runnableA = new Runnable() {
public void run() {
deadLock1.eat(deadLock2);
}
};
Thread t1 = new Thread(runnableA, "runA");
t1.start();
Runnable runnableB = new Runnable() {
public void run() {
deadLock2.eat(deadLock1);
}
};
Thread t2 = new Thread(runnableB, "runB");
t2.start();
}
}
可重入内置锁
可重入性的体现
public synchronized void methodA(){
this.methodB();
}
public synchronized void methodB(){
}
- synchronized是一个可重入锁