zoukankan      html  css  js  c++  java
  • Semaphore使用与源码进阶

     
    一、使用场景
       主要是用来控制同时执行线程的数量,用以保护临界资源
     
     
    二、使用实例
    package com.test.lock;
    
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    
    public class SemphoreTest4 {
    
        static Logger logger = LoggerFactory.getLogger(SemphoreTest4.class ) ;
    
        public static void main(String[] args) throws InterruptedException {
            Semaphore semphore = new Semaphore(2);
            for (int i = 1; i <=29 ; i++) {
                new Thread(()->{
                    try {
                        logger.info(String.format("t %s准备完成", Thread.currentThread().getName() ));
                        semphore.acquire();
                        logger.info(String.format(" 线程 %s 正在执行。。。。。", Thread.currentThread().getName()));
                        TimeUnit.SECONDS.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    semphore.release();
                    logger.info(String.format("t %s 执行结束", Thread.currentThread().getName()));
                },"t"+(i+20)).start(); ;
            }
    
        }
    }
    三、源码主要方法解读
    1)构造方法
    public Semaphore(int permits) {
        //设置AQS中status的值
        sync = new NonfairSync(permits);
    }

    2)semphore.acquire();

    public void acquire() throws InterruptedException {
        //当前线程获取执行令牌
        sync.acquireSharedInterruptibly(1);
    }

    3) 可以执行还是需要去排队

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0) //获取执行权限
            doAcquireSharedInterruptibly(arg); //排队
    }

     4)尝试获取执行权限

    protected int tryAcquireShared(int acquires) {
        for (;;) {
            //如果队列里面已经有排队的,则直接返回-1 去排队
            if (hasQueuedPredecessors())
                return -1;
            //获取当前的状态
            int available = getState();
            //计算剩余的许可数量
            int remaining = available - acquires;
            if (remaining < 0 || //如果<0 则返回去排队
                compareAndSetState(available, remaining)) //如果获取许可 成功,则返回去执行
                return remaining;
        }
    }
    5)线程执行完成,则释放许可
     
    //循环+CAS数据最终的一致性
    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;
        }
    }

     6) 释放许可以后需要唤醒下面线程

    private void doReleaseShared() {
        
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

     四、整体思路是:

       1 设置标识位
       2 线程过来获取许可,如果可以获取到,则继续执行
       3 如果获取失败,则进入队列等待,直到被唤醒
       4 释放许可,并唤醒后面线程
  • 相关阅读:
    防止表单重复提交的几种策略
    Linux模块
    ASP.Net MVC3 图片上传详解(form.js,bootstrap)
    在ASP.NET MVC3 中利用Jsonp跨域访问
    C# 利用反射动态创建对象——带参数的构造函数和String类型
    第一章 CLR 的执行模型
    Linux Shell脚本攻略 读书笔记
    使用MVC4,Ninject,EF,Moq,构建一个真实的应用电子商务SportsStore
    验证码识别的一些总结及相关代码
    使用DateTime的ParseExact方法实现特殊日期时间的方法详解(转)
  • 原文地址:https://www.cnblogs.com/lean-blog/p/13736073.html
Copyright © 2011-2022 走看看