不要使用字符串常量作为对象锁去同步代码
synchronized是对象锁,但是对于这个Java内置的对象锁如果理解不深入可能就会踩到坑。请看下面的例子:
public class DeadLockDemo {
private static String A = "A";
private static String B = "B";
public static void main(String[] args) {
new DeadLockDemo().deadLock();
}
private void deadLock() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (A) {
try { Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("1");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (B) {
synchronized (A) {
System.out.println("2");
}
}
}
});
t1.start();
t2.start();
}
}
上面是一个死锁的例子,用来演示死锁是没有问题,但是大家有没有注意privat static String A = "A",它使用字符串常量作为对象锁,大家认为这个有问题吗?请看下面这个例子。
public class NotifyTest {
private String flag = "true";
class NotifyThread extends Thread {
public NotifyThread(String name) {
super(name);
}
public void run() {
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (flag) {
//flag的对象变为false,对象发生改变
flag = "false";
flag.notifyAll();
}
}
}
class WaitThread extends Thread {
public WaitThread(String name) {
super(name);
}
public void run() {
synchronized (flag) {
while (flag.equals("true")) {
System.out.println(getName() + " begin waiting!");
long waitTime = System.currentTimeMillis();
try {
flag.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
waitTime = System.currentTimeMillis() - waitTime;
System.out.println("wait time :" + waitTime);
}
System.out.println(getName() + " end waiting!");
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("Main Thread Run!");
NotifyTest test = new NotifyTest();
NotifyThread notifyThread = test.new NotifyThread("notify01");
WaitThread waitThread01 = test.new WaitThread("waiter01");
notifyThread.start();
waitThread01.start();
}
}
运行结果:
Main Thread Run!
waiter01 begin waiting!
Exception in thread "notify01" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at juc.NotifyTest$NotifyThread.run(NotifyTest.java:24)
为什么会出现上面的情况:问题主要是flag对象的引用发生了改变,我们如何选择synchoronize的锁呢,其实只要我们将flag换为javaBean就行,现在为了方便起见我们使用String数组private String flag[] = {"true"}这样也可以解决问题。例子如下:
package juc;
/**
* Created by wjk on 2017/7/25.
*/
public class NotifyTest {
private String flag[] = {"true"};
class NotifyThread extends Thread {
public NotifyThread(String name) {
super(name);
}
public void run() {
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (flag) {
//flag的对象变为false,对象发生改变
flag[0] = "false";
flag.notifyAll();
}
}
}
class WaitThread extends Thread {
public WaitThread(String name) {
super(name);
}
public void run() {
synchronized (flag) {
while (flag[0].equals("true")) {
System.out.println(getName() + " begin waiting!");
long waitTime = System.currentTimeMillis();
try {
flag.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
waitTime = System.currentTimeMillis() - waitTime;
System.out.println("wait time :" + waitTime);
}
System.out.println(getName() + " end waiting!");
}
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("Main Thread Run!");
NotifyTest test = new NotifyTest();
NotifyThread notifyThread = test.new NotifyThread("notify01");
WaitThread waitThread01 = test.new WaitThread("waiter01");
notifyThread.start();
waitThread01.start();
}
}