八个锁问题
当多个线程访问同一个对象的synchronized方法时,可能会产生八种不同的锁问题:
- 对于普通同步方法,锁是当前实例对象。
- 对于静态同步方法,锁是当前类的Class对象。
- 对于同步方法块,锁是synchronized括号里配置的对象。
- 对于一个普通方法,即非同步方法,不会产生锁问题,不需要等待锁,直接访问方法即可。
- 对于一个静态方法,即使非同步方法,也会存在锁问题。因为静态方法是属于类的,而不是属于实例的,所以静态方法的锁是当前类的Class对象。
- 当一个线程执行的代码出现异常时,锁会被自动释放。
- synchronized锁住的代码块不是原子操作。如果同一个线程执行两次synchronized代码块,它将获得锁两次。
- 如果两个及以上线程同时等待某个monitor对象,那么只有一个线程可以继续执行,其他线程将被阻塞。
解决办法
下面分别对八个锁问题给出相应的解决办法。
1. 对于普通同步方法,锁是当前实例对象。
解决办法是在方法前面加上synchronized
关键字。
public synchronized void add(){...}
2. 对于静态同步方法,锁是当前类的Class对象。
解决办法是在方法前面加上synchronized
关键字,同时锁定当前类。
public static synchronized void add(){...}
3. 对于同步方法块,锁是synchronized括号里配置的对象。
解决办法是在同步代码块前面加上synchronized
关键字,同时锁定指定的对象。例如:
public void add(){
synchronized(lock){
...
}
}
4. 对于普通非同步方法,不会产生锁问题,无需解决。
直接访问即可。
5. 对于静态非同步方法,也会存在锁问题。
解决办法是锁定当前类的Class对象,可以使用synchronized(Class)
。
public static void add(){
synchronized(ClassName.class){
...
}
}
6. 当一个线程执行的代码出现异常时,锁会被自动释放。
无需解决,JVM会自动释放锁。
7. synchronized锁住的代码块不是原子操作。
解决办法是使用同步方法而不是同步代码块。
public synchronized void add(){
...
}
8. 如果两个及以上线程同时等待某个monitor对象,那么只有一个线程可以继续执行,其他线程将被阻塞。
解决办法是通过使用notify()
或者notifyAll()
通知等待线程,从而释放对象锁。
public synchronized void add(){
...
notify();
}
下面是一个使用synchronized
解决八个锁问题的示例代码:
public class MyObject {
private static int count1 = 0;
private int count2 = 0;
private Object lock = new Object();
public static synchronized void method1() {
// 锁定当前类,等价于 synchronized(MyObject.class)
System.out.println("Method 1 start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void method2() {
// 锁定当前实例对象,等价于 synchronized(this)
System.out.println("Method 2 start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public void method3() {
// 锁定指定对象
synchronized (lock) {
System.out.println("Method 3 start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 3 end");
}
}
public static void method4() {
// 锁定当前类,等价于 synchronized(MyObject.class)
synchronized (MyObject.class) {
System.out.println("Method 4 start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 4 end");
}
}
public static synchronized void method5() {
// 锁定当前类,等价于 synchronized(MyObject.class)
count1++;
System.out.println("Method 5 count1=" + count1);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void method6() {
// 锁定当前实例对象,等价于 synchronized(this)
count2++;
System.out.println("Method 6 count2=" + count2);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void method7() {
// 锁定当前类,等价于 synchronized(MyObject.class)
synchronized (MyObject.class) {
System.out.println("Method 7 start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 7 end");
}
}
public static synchronized void method8() {
// 锁定当前类,等价于 synchronized(MyObject.class)
System.out.println("Method 8 start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 8 end");
}
}
文章评论