自旋锁
实现原理(while循环+CAS(compareAndSet))
  • 一个锁已经被一个线程锁持有,其他尝试获取该锁的线程并不会立即阻塞,而是采用循环的方式不停地尝试去获取锁,直至获取成功,跳出循环
  • 现实生活中的例子:公用洗衣机,一个人想去洗衣服,但是此时洗衣机正在被占用着,那么这个人有两种选择,要么一直在洗衣机前等待,等到该洗衣机洗完,另一种每隔一段时间再回来看看,洗衣机是否已经洗完
代码示例和解释
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 自旋锁:
 * 实现原理(while循环+CAS(compareAndSet))
 * 一个锁已经被一个线程锁持有,其他尝试获取该锁的线程并不会立即阻塞,而是采用循环的方式不停地尝试去获取锁,直至获取成功,跳出循环
 * 现实生活中的例子:公用洗衣机,一个人想去洗衣服,但是此时洗衣机正在被占用着,那么这个人有两种选择,要么一直在洗衣机前等待,等到该洗衣机洗完,另一种每隔一段时间再回来看看,洗衣机是否已经洗完
 */
public class SpinLock {
    
	public static void main(String[] args){
		SpinLockResource spinLockResource = new SpinLockResource();
    	new Thread(()->{
    		//t1线程获得锁
    		spinLockResource.getLock();
    		//睡眠5秒,让t2线程尝试获取锁
    		try {
				Thread.sleep(5000);
			} catch (Exception e) {
				e.printStackTrace();
			}
    		//t1线程释放锁
    		spinLockResource.removeLock();
    	},"t1").start();
    	
    	//当前main线程暂停一秒
    	try {
			TimeUnit.SECONDS.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
    	
    	new Thread(()->{
    		//t2线程去获取锁
    		spinLockResource.getLock();
    		//睡眠1秒,t2线程获取完锁
    		try {
				Thread.sleep(1000);
			} catch (Exception e) {
				e.printStackTrace();
			}
    		//t2线程释放锁
    		spinLockResource.removeLock();
    	},"t2").start();
    	
    }
}

//资源类
class SpinLockResource{
	
	//原子引用线程,使用原子类的CAS方法
	AtomicReference<Thread> atomicThread = new AtomicReference<>();
	
	//获取锁方法
	public void getLock(){
		Thread thread = Thread.currentThread();//获取当前线程
		//atomicThread.compareAndSet(null, thread)
		//当前线程去和主内存中线程比较,如果此时主内存中线程为空,返回true,将当前线程设置进主内存中
		//初始atomicThread.compareAndSet(null, thread)为true,当前线程会设置进主内存,整个取反为false,不会进入while循环
		while(!atomicThread.compareAndSet(null, thread)){
			System.out.println(Thread.currentThread().getName()+"\t try to getLock...");
		}
		System.out.println(Thread.currentThread().getName()+"\t getLock......");
	}
	
	//释放锁资源的方法
	public void removeLock(){
		Thread thread = Thread.currentThread();//获取当前线程
		atomicThread.compareAndSet(thread, null);//主内存中为当前线程,将主内存中线程置为空,相当于释放了锁资源,供其他线程写入。
		System.out.println(Thread.currentThread().getName()+"\t removeLock......");
	}
	
}
打印结果:

t1线程获取锁之后,t2线程会循环尝试去获取锁,而不是一直阻塞

java自旋转重试 java实现自旋锁_跳出循环


直到t1线程释放锁,t2线程才去跳出循环,获取锁

java自旋转重试 java实现自旋锁_并发编程_02


但是如果出现极端情况下,第一个线程一直没有释放锁,第二个线程不停地去循环,造成死循环,就会造成很大的cpu开销