1、什么是死锁
我们假设有两个线程A和B,两者的run,都要去访问两个对象,A要访问X和Y,B要访问X和Y,这个时候,A会给X和Y加上自己的互斥锁,而B也会给X和Y加上互斥锁,A要首先给X加上锁,然后再去争取锁住Y,而这时,B一看X被锁了,就率先锁住了Y,这个时候,A在等着B释放Y,B在等着A释放X,两者就相当于死锁了,谁都需要对方退一步,相同的是谁也不会退一步。
看一段代码来实现上述情况:
public class TestThread01 implements Runnable{
public int flag =1;
static Object x =new Object();
static Object y =new Object();
public void run(){
System.out.println("flag="+flag);
if(flag==1){
synchronized (x) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (y) {
System.out.println("1");
}
}
}
if(flag==0){
synchronized (y) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (x) {
System.out.println("2");
}
}
}
}
public static void main(String[] args) {
TestThread01 t1 = new TestThread01();
TestThread01 t2 = new TestThread01();
t1.flag=1;
t2.flag=0;
Thread t11 = new Thread(t1);
Thread t22 = new Thread(t2);
t11.start();
t22.start();
}
}
上述代码其实就是一个很好的死锁的例子,t11和tt2两个线程,都会去执行run方法,一个去执行flag为1的情况,一个去执行flag为2的情况,而这两个情况里,会产生之前所述的死锁。
运行结果也就是只会输出flag=1和flag=2两句话,之后就会死住。
2、一道死锁相关的面试题
public class TestThread01 implements Runnable{
int b =100;
public synchronized void m1(){
b=1000;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("b = "+b);
}
public void m2(){
System.out.println(b);
}
public void run(){
m1();
}
public static void main(String[] args) {
TestThread01 t1 = new TestThread01();
Thread t11 = new Thread(t1);
t11.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t1.m2();
}
}
请问如上代码的输出结果:
m1方法被锁定了,请问t1的m2方法输出的结果是初始化的100,还是修改后的1000?
答案是1000,虽然m1被锁定了,t11线程在调用m1之后将b改为1000,然后休眠5秒,但是t1对象访问的并不是锁定的m1,而是m2,m2输出当前内存中的b的值,所以,我们要记住,被锁定的方法不能被访问,但是其余的变量和方法,我们是可以访问的。
我们将上面的代码修改一下,如下:
public class TestThread01 implements Runnable{
int b =100;
public synchronized void m1(){
b=1000;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("b = "+b);
}
public void m2(){
Thread.sleep(2500);
b = 2000;
}
public void run(){
m1();
}
public static void main(String[] args) {
TestThread01 t1 = new TestThread01();
Thread t11 = new Thread(t1);
t11.start();
t1.m2();
}
}
这段代码我们将m2也设置成了修改b为2000,那么m1修改b为1000,最后结果是多少呢?
分析一下:开了一个线程t11,t11调用run,run调用m1,修改b为1000后,休眠5秒,这个时候,我们调用了m2,修改b为2000,输出结果也是2000,这也说明了,我们锁定了m1方法,但是依旧能去修改b,所以这也就告诉我们,我们要把访问涉及到同个资源的方法,都要去加锁。
例如:我们将m2修改为加上锁的m2,那么再去运行,b就是1000,因为线程在执行m1,m1被锁,因为涉及到了b,所以m2无法去锁定,也就没法去执行。
但是,我们在main方法里去输出一下:System.out.print(t1.b);这个时候,等待m1释放对b的锁之后,m2就可以执行了,所以输出为2000.