1、重入锁
- 重入锁:能够让线程进行重入的锁叫作重入锁,也叫递归锁,在JAVA环境下 ReentrantLock 和synchronized 都是可重入锁。
- 锁重入:某个线程获得一个已经由它自己持有的锁对象,那么这个请求就会成功,即重入。重入是对本线程来说,即本线程多资源可以多次加锁进入,而不会出现阻塞。
- 锁重入现象:
1.当进入a方法的时候,线程必须先拿到当前类的实例(锁),这个时候,a的方法内同时调用了b方法,b方法也是用synchronized来修饰的,所以b方法也需要线程获取锁才能让线程进来。这时候会不会发生进入了a方法,而不能进入b方法的现象呢?
答案是:不会,因为进入a方法的时候,线程拿的锁是当前类的实例(锁),当线程要进入b方法的时候,线程拿的锁也是当前类的实例(锁),前一把锁和后一把锁都是同一个把锁,这个时候就发生了锁重入现象
2.当一个线程拿到当前类的实例(锁)调用a方法,这个时候另外一个线程调用b方法,会不会等待?
答案是:如果两个线程里是是同一个对象调用方法,才能够锁的住,需要等待,如果是不同的对象锁不住,不需要等待。
public class Demo {
public synchronized void a () {
System.out.println("a");
b();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void b() {
System.out.println("b");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Demo d1= new Demo();
Demo d2= new Demo();
new Thread(new Runnable() {
@Override
public void run() {
d1.a();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
//d1.b();//需要等待,因为d1的锁已经被另外一个线程拿到了
d2.b();//不用等待,拿到了d2的锁
}
}).start();
}
}
2、自旋锁
可以理解成线程进入了while(true){}中,CPU在空转,消耗性能。一般发生在多线程需要进入synchronized修饰的方法,只有一个线程能获取锁,其他线程很有可能都发生自旋现象。
/**
* 多个线程执行完毕之后,打印一句话,结束
*/
public class Demo2 {
public static void main(String[] args) {
//第一个线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程执行...");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程执行完毕了...");
}
}).start();
//第二个线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程执行...");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程执行完毕了...");
}
}).start();
//第三个线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程执行...");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程执行完毕了...");
}
}).start();
//第四个线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程执行...");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程执行完毕了...");
}
}).start();
//第五个线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程执行...");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程执行完毕了...");
}
}).start();
while(Thread.activeCount() != 1) {//当前活动线程的数量,程序没有执行完毕
// 自旋,空转CPU
}
System.out.println("所有的线程执行完毕了...");
}
}
3、死锁
源互斥导致互相占用不放
public class Demo3 {
private Object obj1 = new Object();
private Object obj2 = new Object();
public void a () {
synchronized (obj1) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2) {
System.out.println("a");
}
}
}
public void b () {
synchronized (obj2) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1) {
System.out.println("b");
}
}
}
public static void main(String[] args) {
Demo3 d = new Demo3();
new Thread(new Runnable() {
@Override
public void run() {
d.a();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
d.b();
}
}).start();
}
}