1、公平锁,非公平锁
公平锁:非常公平,不能够插队,必须先来后到
非公平锁:非常不公平,可以插队(默认的都是非公平锁)
public ReentrantLock() { //非公平锁,默认
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {//公平锁,new ReentrantLock(true),参数改为true时为公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
2、可重入锁(递归锁)-------所有的锁,都是可重入锁
概念:拿到了最外层的锁之后,就可以拿到了后面的锁(这是自动获得的)。举例:拿到了家门口的钥匙,就可以打开家里房间的门。
synchronized版
package lin.lock;
import java.util.concurrent.TimeUnit;
//Synchronized==========================================================
public class ReenTrantLock_ {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sms();
},"A").start();
/*try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone{
public synchronized void sms(){
System.out.println(Thread.currentThread().getName()+" sms");
call(); //这里也有锁
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName()+" call");
}
}
执行结果:
lock版
package lin.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//Lock锁==========================================================
public class ReenTrantLock_Demo02 {
public static void main(String[] args) {
Phone2 phone = new Phone2();
new Thread(()->{
phone.sms();
},"A").start();
new Thread(()->{
phone.sms();
},"B").start();
}
}
class Phone2{
Lock lock = new ReentrantLock();
public void sms(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+" sms");
call(); //这里也有锁
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void call(){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+" call");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
执行结果:
细节问题:ReentrantLock版,当调用sms()这个方法时,拿到了lock.lock()这把锁,这时又调用了call()方法,此时又拿到call()的lock.lock()锁,然后lock.unlock()解锁,然后才会去执行sms()中的lock.unlock()解锁!(在lock锁中必须要配对,否则就会死锁)
3、自旋锁(不断地去循环。遍历,迭代知道成功为止)
什么是自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
自己写一个基础的自旋锁,小测试:
package lin.lock;
import java.util.concurrent.atomic.AtomicReference;
/*
* 自旋锁
* */
public class SpinLockDemo01 {
//Thread引用类型,默认的是null
AtomicReference<Thread> atomicReference = new AtomicReference<>();
//加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"------ myLock");
//自旋锁
while(!atomicReference.compareAndSet(null,thread)){
}
}
//解锁
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"------ myUnLock");
atomicReference.compareAndSet(thread,null);
}
}
package lin.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TestSpinLock {
public static void main(String[] args) {
// ReentrantLock lock = new ReentrantLock();
// lock.lock();
// lock.unlock();
//底层就是CAS实现的自旋锁
SpinLockDemo01 lock = new SpinLockDemo01();
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.myUnLock();
}
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.myUnLock();
}
},"B").start();
}
}
注意:A线程进来之后,会先拿到这个锁,线程B必须等待。然后A线程自旋,等A线程解锁之后,B线程才有资格解锁。(因为A解锁之后才会释放,释放之后B才有机会进去拿到锁,然后再解锁)
4、死锁
死锁是什么?
所谓死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
死锁产生的4个必要条件?
产生死锁的必要条件:
互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。
死锁的例子
package lin.lock;
import java.util.concurrent.TimeUnit;
public class DeadLockDemo01 {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA,lockB),"T1").start();
new Thread(new MyThread(lockB,lockA),"T2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized(lockA){
System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"=>"+lockB);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"=>"+lockA);
}
}
}
}
结果:
遇到死锁,如何解决问题(因为在控制台中,没有显示任何的异常,只是单纯的卡着程序不懂而已,如何排查错误!!!)
- 使用 jps -l 定位进程号(前面的一串数字就是进程号)
- 使用 jstack 进程号 找到死锁问题