1 如何保证原子操作
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
这里的available 和remaining是可能多个线程同时访问的,如果在一个线程的一次循环中间被别的线程把信号量的值修改了后,会再次循环,而此时available 和remaining的值是重新获取的,通过cas就保证了正确性。
2 共享锁和独占锁的区别:
共享锁:一个线程获取锁成功后,出队的时候就去唤醒下一个线程。
独占锁:线程释放锁时才会去唤醒下一个线程。
3 sun.misc.Unsafe#park 原理分析
Semaphore 信号量:
是通过AQS的共享锁模式实现的,底层基于Locksupport.park,和unpark来实现。
使用场景:对可重复使用的公共资源进行数量控制。经典的例子是,多个车排队使用固定数目的车位。当车位都被使用时,再次进来的线程会被放入同步队列,此时的同步队列是双向链表。支持以非公平或公平的方式获取车位资源。许可的数量,会被设置为同步队列的state状态值。
获取锁:当资源都被占用时,再进来的线程会被封装成共享节点,以自旋的方式放入到同步队列。如果同步队列为空,会先创建一个空的head节点,然后把当前线程的节点设置为head的next。跳出入队的自旋,然后以自旋的方式去获取锁,如果没有获取成功,就把前一个节点的waitstate由0改为-1(NODE.SIGNAL),标示此节点后面由节点。第二次循环的时候,就把自己通过Locksupport.park把当前线程阻塞了。
释放锁:当一个线程使用完资源时,先通过自旋的方式释放锁,然后再把head的waitstate由-1(NODE.SIGNAL)改为0.然后再以LockSupport.unpark(s.thread)的方式,唤醒head的next节点对应的线程。被唤醒的线程通过自旋的方式尝试去获取锁,如果获取到了后,把当前节点设置为head节点,并把此节点的prev设置为null,把此节点对应的线程设置为null。把当前的节点的waitstate由-1设置为0.如果是共享状态的节点,就继续往后唤醒。
/**
* Synchronization implementation for semaphore. Uses AQS state
* to represent permits. Subclassed into fair and nonfair
* versions.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
setState(permits);
}
final int getPermits() {
return getState();
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))
return true;
}
}
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
}
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}
/**
* NonFair version
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
/**
* Fair version
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
实例:
public class SemaphoreTest {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
threadPoolExecutor.execute(new NoFairInfomationThread(new PersonInfo("name"+(i+1),(i+1)),nofairSemaphore));
}
threadPoolExecutor.shutdown();
System.out.println("nofair end");
for (int i = 11; i < 20; i++) {
threadPoolExecutor2.execute(new FairInfomationThread(new PersonInfo("name"+(i+1),(i+1)),fairSemaphore));
}
threadPoolExecutor2.shutdown();
System.out.println("fair end");
}
static Semaphore nofairSemaphore = new Semaphore(3);
static Semaphore fairSemaphore = new Semaphore(3,true);
static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,10,100,TimeUnit.SECONDS,new LinkedBlockingDeque<>());
static ThreadPoolExecutor threadPoolExecutor2 = new ThreadPoolExecutor(10,10,100,TimeUnit.SECONDS,new LinkedBlockingDeque<>());
static class NoFairInfomationThread extends Thread{
private PersonInfo personInfo;
public NoFairInfomationThread(PersonInfo personInfo,Semaphore semaphore){
this.personInfo = personInfo;
}
public void run(){
try {
nofairSemaphore.acquire();
System.out.println("nofair "+personInfo.toString());
System.out.println("nofair 现在可用的信号量为:"+nofairSemaphore.availablePermits());
System.out.println("nofair 先在的时间为:"+System.currentTimeMillis());
Thread.sleep(5000);
nofairSemaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class FairInfomationThread extends Thread{
private PersonInfo personInfo;
public FairInfomationThread(PersonInfo personInfo,Semaphore semaphore){
this.personInfo = personInfo;
}
public void run(){
try {
fairSemaphore.acquire();
Thread.sleep(1000);
System.out.println(personInfo.toString());
System.out.println("现在可用的信号量为:"+nofairSemaphore.availablePermits());
System.out.println("先在的时间为:"+System.currentTimeMillis());
fairSemaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class PersonInfo{
private String name ;
private int age ;
public PersonInfo(String name ,int age){
this.name =name ;
this.age = age ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "name = "+this.getName()+" ; age = "+this.getAge();
}
}
}
CountDownLatch 如果已经放开了,后面就控制不住了
是基于AQS的共享锁实现的,底层基于LockSupport.park和unpark来实现。使用LockSupport的好处是可以控制到具体的线程。
使用场景:等多个资源都满足条件时,才执行下一步操作。经常用于多个子线程都完成后,主线程才继续执行。
是通过什么方式,让线程再没达到一定数量时阻塞的?
下面的实例中,主线程通过调用await方法来阻塞,直到所有的一定数量的线程都执行(即调用了countDown方法)后,CountDown的state为0时,最后一个子线程通过LockSupport.unpark来唤醒主线程
实例:
public class CountDownLatchTest {
public static void main(String[] args) {
try {
for (int i = 0; i < 10; i++) {
service.submit(new WorkThread("thread("+(i+1)+")"));
}
System.out.println("main running ...... ");
begin.countDown();
end.await();
System.out.println("All is end !");
service.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static CountDownLatch begin = new CountDownLatch(1);
static CountDownLatch end = new CountDownLatch(10);
final static ExecutorService service = Executors.newFixedThreadPool(10);
static class WorkThread extends Thread{
public WorkThread(String name){
super(name);
}
public void run(){
try {
System.out.println(Thread.currentThread().getName()+" comming ...... ");
begin.await();
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName()+" running ...... ");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
end.countDown();
}
}
}
}
CyclicBarrier
是基于ReentrantLock 和 Condition实现的。
哪个线程唤醒的转换后的同步队列头节点:最后一个线程加锁后,满足了条件,把单向链表的条件队列转换为双向链表的同步队列。解锁的时候去唤醒同步队列的第一个节点
public class CyclicBarrierTest {
static CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
static class CyclicBarrierThread extends Thread{
public void run(){
System.out.println(Thread.currentThread().getName()+" ---> 开始执行了。。。。。。。");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" ---> 跨过了栅栏。。。。。。。");
}
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
CyclicBarrierThread thread = new CyclicBarrierThread();
thread.start();
}
}
}
条件队列:
单向链表
同步队列: