AQS理解之二,自己设计一个锁

AQS理解之二,自己设计一个锁

一,实现锁的条件

首先我们想一想,如果我们自己实现一个类似于java中的锁,我们可能需要哪些必要的东西:

1,记录是哪个线程持有了锁。

2,如果有一个变量代表加锁,A线程加锁,需要让B线程能看见,这里需要可见性,所以我们可能要用到volatile变量。

3,对变量的操作应该是一个原子操作。

二,实现

首先我们定义一个类,这个类需要有一个持有锁的变量。这里使用ownerThread这个变量来代表持有锁的线程。

class MyLock{

	private Thread ownerThread;

	public Thread getOwnerThread() {
		return ownerThread;
	}

	public void setOwnerThread(Thread ownerThread) {
		this.ownerThread = ownerThread;
	}
}

我们先简单定义一下加锁和解锁的方法。并设置一个volatile的变量来保证锁的可见性。

class MyLock{

	private Thread ownerThread;

	private volatile int state;


	public Thread getOwnerThread() {
		return ownerThread;
	}

	public void setOwnerThread(Thread ownerThread) {
		this.ownerThread = ownerThread;
	}

	public boolean lock(){
		//可重入,如果锁设置不可重入,则state应该只能为1和0,并在此处抛出异常。
		if (Thread.currentThread() == getOwnerThread()){
			//临时写法,应该为原子操作的++;
			state++;
			return true;
		} else if(state == 0){
			state++; //这里的判断相等和++操作不应该分开,临时写法。正确方法应该为CAS操作。
			setOwnerThread(Thread.currentThread());
			return true;
		}
		return false;
	}

	public void unlock(){
		if (Thread.currentThread() != getOwnerThread()){
			throw new RuntimeException("不是锁持有线程,不能解锁");
		}
		state--;
		setOwnerThread(null);
	}
}

这里我们看到lock里我们考虑到了可重入性,所以简单实现了一下,如果还要实现lock阻塞,应该加上自旋操作,如:

public boolean lock(){
	//加上自旋,如果失败,继续。
	for (;;) {
		if (Thread.currentThread() == getOwnerThread()){
			state++;
			return true;
		}else if(state == 0){
			state++;
			setOwnerThread(Thread.currentThread());
			return true;
		}
	}
}

我们继续完善代码,将之前的临时操作进一步优化。因为我们无法使用unsafe类来操作,我门这里将state修改为AtomicInteger。

class MyLock{

	private Thread ownerThread;

	private volatile AtomicInteger state;


	public MyLock(){
		state = new AtomicInteger(0);
	}

	public Thread getOwnerThread() {
		return ownerThread;
	}

	public void setOwnerThread(Thread ownerThread) {
		this.ownerThread = ownerThread;
	}

	public boolean lock(){
		//可重入
		for (;;) {
			if (Thread.currentThread() == getOwnerThread()){
				state.incrementAndGet();
				return true;
			}else if(state.compareAndSet(0,1)){
				setOwnerThread(Thread.currentThread());
			}
		}
	}

	public void unlock(){
		if (Thread.currentThread() != getOwnerThread()){
			throw new RuntimeException("不是锁持有线程,不能解锁");
		}
		setOwnerThread(null);
		state.decrementAndGet();
	}
}

三,验证

public static void testSout(){
	try{
		lock.lock();
		System.out.println("a");
		Thread.sleep(100);
		System.out.println("b");
		Thread.sleep(100);
		System.out.println("c");
		Thread.sleep(100);
		System.out.println("d");
		Thread.sleep(100);
	}catch (Exception e){
		e.printStackTrace();
	}finally {
		lock.unlock();
	}
}

public static void main(String[] args) throws InterruptedException {

	Runnable runnable = new Runnable() {
		@Override
		public void run() {
			TestUtil.testSout();
		}
	};
	Thread thread = new Thread(runnable);
	Thread thread1 = new Thread(runnable);
	Thread thread2 = new Thread(runnable);
	Thread thread3 = new Thread(runnable);
	thread.start();
	thread1.start();
	thread2.start();
	thread3.start();
	thread.join();
	thread1.join();
	thread2.join();
	thread3.join();

}

控制台可以正常输出,说明我们加的锁是可以用的。

四,总结

在这里我们实现的是一个可重入的,不公平,利用自旋来阻塞的一个锁,后续继续深入。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值