Semaphore&CyclicBarrier源码分析

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();
        }

    }
}


    
条件队列:
    单向链表
    
同步队列:

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值