(四)【Java精选面试题】AQS底层架构设计原理(含答案)

追求适度,才能走向成功;人在顶峰,迈步就是下坡;身在低谷,抬足既是登高;弦,绷得太紧会断;人,思虑过度会疯;水至清无鱼,人至真无友,山至高无树;适度,不是中庸,而是一种明智的生活态度。

导读:本篇文章讲解 (四)【Java精选面试题】AQS底层架构设计原理(含答案),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文


1. 谈谈Lock锁底层实现原理

底层基于AQS+Cas+LockSupport锁实现

2. synchronized与lock锁之间区别

Lock基于AQS封装的锁,结合 CAS实现,而Lock锁的升级过程需要自己实现;
Synchronized是基于C++虚拟机封装,JDK1.6优化可以实现锁的升级过程;

3. 谈谈LockSupport的用法

public class Test01 {
	public static void main(String[] args) {
		Thread t1 = new Thread(() -> {
			System.out.println(">>>start<<<");
			//park停止线程
			LockSupport.park();
			System.out.println(">>>end<<<");
		});
		t1.start();
		try {
			Thread.sleep(1000);
		} catch (Exception e) {
		
		}
		// unpark(启动唤醒线程)
		LockSupport.unpart(t1);
	}
}

4. AQS核心参数有哪些呢

1.Node节点采用双线链表的形式存放正在等待的线程waitStatus状态、thread等到锁的线程
CANCELLED,值为1,表示当前的线程被取消;
SIGNAL,值为-1,释放资源后需唤醒后继节点;
CONDITION,值为-2,等待condition唤醒;
PROPAGATE,值为-3,工作于共享锁状态,需要向后传播,比如根据资源是否剩余,唤醒后继节点;
值为0,表示当前节点在sync队列中,等待着获取锁。
1.Head头节点,等待队列的头节点
2.Tail尾结点,正在等待的线程
3.State锁的状态,0无锁、1已经获取锁,当前线程重入不断+1
4.exclusiveOwnerThread记录锁的持有
核心方法:
//充实获取锁,如果小于0的情况则当前线程会追加到链表中
1.tryAcquireShared(arg)
//如果为true的情况下,则开始唤醒阻塞的线程
2.tryReleaseShared(arg)

公平锁实现原理图:
在这里插入图片描述

非公平锁实现原理图:
在这里插入图片描述

5. Lock锁基本实现原理

1.Lock锁原理,基于javaAQS类封装,在获取锁的时候AQS类中有一个状态state+1,当前线程不断重入的时候都会不断+1,当在释放锁的时候state-1;最终state为0该锁没有被任何线程获取到,没有抢到锁的线程,会存在一个双向的链表中。
2.公平锁与非公平锁在获取锁的时候多了一个判断(!hasQueuedPredecessors()&
阻塞和唤醒用的apilocksupport,为了这个效率只会唤醒阻塞队列中head节点.next线程。

public class DemoLock {
	// cas 0表示没有获取锁 1表示获取到锁
	private AtomicInteger atomicState = new AtomicInteger(0);
	// 缓存没有获取锁的线程
	private ConcurrentLinkedDeque<Thread> waitDequeThread = new ConcurrentLinkedDeque<Thread>();
	// 获取锁的线程
	private transient Thread exclusiveOwnerThread;

	public boolean lock() {
		boolean acquire = acquire();
		if (acquire) {
			// 记录获取到锁的线程
			exclusiveOwnerThread = Thread.currentThread();
			return true;
		}
		// 尝试重试策略 最多重试5次
		int i = 0;
		for(;;) {
			if (acquire()) {
				return true;
			}
			i++;
			// 限制最多只能重试5次
			if (i >= 5) {
				// 获取锁失败,则将该线程缓存到链表中
				Thread thread = Thread.currentThread();
				waitDequeThread.add(thread);
				// 当前线程释放cpu执行权
				LockSupport.park();
				return false;
			}
		}
	}

	/**
	* 获取锁
	* @return
	*/
	public boolean acquire() {
		Integer state = getState();
		if (state == 0) {
			// 获取锁
			if (compareAndSet(0, 1)) {
				return true;
			}
			// cas 失败!
			return false;
		}
		return false;
	}

	public void unLock() {
		if (exclusiveOwnerThread != Thread.currentThread()) {
			return;
		}
		// 释放锁
		if (compareAndSet(1, 0)) {
			// 唤醒正在阻塞的第一个线程
			Thread thread = waitDequeThread.peekFirst();
			if (thread != null) {
				LockSupport.unpark(thread);	
			}
			return;
		}
	}

	private boolean compareAndSet(int expect, int update) {
		return atomicState.compareAndSet(expect, update);
	}

	private Integer getState() {
		return atomicState.get();
	}

	public static void main(String[] args) {
		DemoLock demoLock = new DemoLock();
		demoLock.lock();
		System.out.println("test");
		new Thread(() -> {
			demoLock.lock();
			System.out.println(Thread.currentThread().getName() + "我是子线程");
		}).start();	
//		demoLock.unLock();
	}
}

6. Semaphore信号量底层原理

1.Semaphore用于限制可以访问某些资源(物理或逻辑)的线程数目,他维护了一个许可证集合,有多少资源需要限制就维护多少许可证集合,假如这里有N个资源,那就对应于N个许可证,同一时刻也只能有N个线程访问。一个线程获取许可证就调用acquire方法,用完了释放资源就调用release方法。
2.可以简单理解为Semaphore信号量可以实现对接口限流,底层是基于aqs实现
Semaphore简单用法

Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 6; i++) {
	new Thread(() -> {
		try {
			// 获取凭证 状态-1
			semaphore.acquire();	
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + ",抢到了凭据");
		try {
			Thread.sleep(1000);
			System.out.println(Thread.currentThread().getName() + ",释放凭据");
			// 状态+1
			semaphore.release();	
		} catch (Exception e) {}
	}).start();	
}

Semaphore工作原理

  1. 可以设置Semaphore信号量的状态state值为5
  2. 当一个线程获取到锁的情况下,将state-1,释放成功之后state+1
  3. 当state<0,表示没锁的资源,则当前线程阻塞。

7. CountDownLatch底层原理

CountDownLatch源码分析
CountDownLatch是一种java.util.concurrent包下一个同步工具类,它允许一个或多个线程等待直到在其他线程中一组操作执行完成。和join方法非常类似
CountDownLatch底层是基于AQS实现的
CountDownLatch countDownLatch = new CountDownLatch(2)AQS的state状态为2
调用countDownLatch.countDown();方法的时候状态-1,当AQS状态state为0的情况下,则唤醒正在等待的线程。

public static void main(String[] args) {
	CountDownLatch countDownLatch = new CountDownLatch(1);
	new Thread(() -> {
		try {
			System.out.println("t1开始执行..");	
			countDownLatch.await();
			System.out.println("t1结束执行..");
		} catch (InterruptedException e) {
			e.printStackTrace();	
		}
	}, "t1").start();
	countDownLatch.countDown();
	countDownLatch.countDown();
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/131168.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!