总结锁
公平锁与非公平锁
公平锁:非常公平,不可以插队
非公平锁:不公平,可以插队,谁能力强,把谁优先
可重入锁
什么是可重入锁?
-
可重入锁,也被称为递归锁,它指的是在同一个线程中,当外层方法获取锁的时候,如果内层方法也需要获取同一把锁,那么内层方法会自动获取到锁,不会因为外层方法已经获取过该锁且尚未释放而导致线程阻塞
-
synchronized关键字和ReentrantLock都是典型的可重入锁实现。
-
当线程重复获取同一个锁时,如果是不可重入锁,那么每次获取锁都需要进行线程同步,这会增加不必要的性能开销。而可重入锁则允许同一个线程多次获取同一把锁,从而避免这种不必要的开销。
-
可重入锁还可以在一定程度上避免死锁的发生
自旋锁
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
-
这个就是自旋锁
-
如果不满足条件就一直旋转,直到满足条件
-
写一个线程自旋锁
public class MyLock {
private static AtomicReference<Thread> atomicReference = new AtomicReference();
Thread thread = Thread.currentThread();
public void myLock() {
//不满足线程为
while (!atomicReference.compareAndSet(null, thread)) {
System.out.println(Thread.currentThread().getName() + "持续获得资源");
}
System.out.println(Thread.currentThread().getName() + "lock");
}
public void myUnLock(){
System.out.println(Thread.currentThread().getName() + "unlock");
atomicReference.compareAndSet(thread, null);
}
}
class Test{
public static void main(String[] args) {
MyLock myLock = new MyLock();
new Thread(()->{
myLock.myLock();
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
myLock.myUnLock();
}
},"a").start();
new Thread(()->{
myLock.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
myLock.myUnLock();
}
},"b").start();
}
}
- 创建一个原子引用,泛型是,Thread线程
- 循环判断是,不满足线程为空,也就是线程资源没有被占用,测试是为了让每次只跑一个线程
- 初始情况下,原子引用的的初始值是为null,
- 所以只用是空的情况下,才会将资源分配给当前线程(Thread thread = Thread.currentThread();)
- 那么当线程a,拿到资源时,线程b,就会进入循环,一直获取资源,知道线程a解锁
死锁
-
当男孩拿了苹果,等他吃完苹果要吃零食
-
女孩吃里零食,他想吃苹果
-
男孩必须拿到零食的锁,才能全部释放
-
女孩必须拿到苹果的锁,才能全部释放
-
于是两个人都拿不到资源,处于死锁状态
public class DeadLock {
public static void main(String[] args) {
Apple apple = new Apple();
Snack snack = new Snack();
Boy boy = new Boy(apple,snack);
Girl girl = new Girl(apple,snack);
new Thread(boy).start();
new Thread(girl).start();
}
}
class Boy implements Runnable{
private Apple apple;
private Snack snack;
public Boy(Apple apple, Snack snack) {
this.apple = apple;
this.snack = snack;
}
@Override
public void run() {
synchronized (apple){
System.out.println("男孩在吃苹果");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("男孩苹果吃完了");
synchronized (snack){
System.out.println("男孩在吃零食");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("男孩零食吃完了");
}
}
}
}
class Girl implements Runnable{
private Apple apple;
private Snack snack;
public Girl(Apple apple, Snack snack) {
this.apple = apple;
this.snack = snack;
}
@Override
public void run() {
synchronized (snack){
System.out.println("女孩在吃零食");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("女孩零食吃完了");
synchronized (apple){
System.out.println("女孩在吃苹果");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("女孩苹果吃完了");
}
}
}
}
class Apple{
}
class Snack{
}
为什么可以保证不会死锁
public class ReetrantLockTest {
public static void main(String[] args) {
Email email = new Email();
new Thread(()->{
email.write();
},"a").start();
new Thread(() -> {
email.write();
},"b").start();
}
}
class Email{
public synchronized void write(){
System.out.println(Thread.currentThread().getName()+"写邮件");
send();
}
public synchronized void send(){
System.out.println(Thread.currentThread().getName()+"发短信");
}
}
- 当出现多个线程对同一个资源操作时,就有可能出现死锁
- 可重入锁是让线程在获得了实例锁之后,不需要再次获得锁,而是继续执行,最后释放锁
- 当使用ReenTrantLock时,锁的时ReententLock对象