thread_Semaphore信号量

 Semaphore也是一个线程同步的辅助类,能够维护当前访问自身的线程个数,并提供了同步机制。
  使用Semaphore能够控制同时访问资源的线程个数,例如,实现一个文件容许的并发访问数。安全

   一个计数信号量。从概念上讲,信号量维护了一个许可集。
若有必要,在许可可用前会阻塞每个 acquire(),而后再获取该许可。每一个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。
可是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采起相应的行动。拿到信号量的线程能够进入代码,不然就等待。并发

 主要方法摘要:
   void acquire():今后信号量获取一个许可,在提供一个许可前一直将线程阻塞,不然线程被中断
   void release():释放一个许可,将其返回给信号量
   int availablePermits():返回此信号量中当前可用的许可数
   boolean hasQueuedThreads():查询是否有线程正在等待获取
1.简单例子dom

 维护当前访问自身的线程个数
@Test
public void semaphore1Test() throws InterruptedException { // 线程池 ExecutorService exec = Executors.newCachedThreadPool(); // 只能5个线程同时访问 final Semaphore semp = new Semaphore(5); // 模拟20个客户端访问 for (int index = 0; index < 20; index++) { final int NO = index; Runnable run = new Runnable() { public void run() { try { // 请求得到许可,若是有可得到的许可则继续往下执行,许可数减1。不然进入阻塞状态 semp.acquire(); System.out.println("Accessing: " + NO); Thread.sleep((long) (Math.random() * 10000)); // 访问完后,释放许可,许可数加1,若是屏蔽下面的语句,则在控制台只能打印5条记录,以后线程一直阻塞 semp.release(); System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3-semp.availablePermits()) + "个并发"); } catch (InterruptedException e) { } } }; exec.execute(run); } // 退出线程池 exec.shutdown(); }

2.当锁工具

   当信号量的数量上限是1时,Semaphore能够被当作锁来使用。经过acquire和release方法来保护关键区域。测试

    @Test
    public void semaphore2Test() throws InterruptedException {
        final Business business = new Business();
        ExecutorService executor = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 3; i++) {
            executor.execute(new Runnable() {
                public void run() {
                    business.service();
                }
            }
            );
        }
        executor.shutdown();
    }
   class Business {
        private int count;
        Lock lock = new ReentrantLock();
        Semaphore sp = new Semaphore(1);
        public void service() {
            // lock.lock();
            try {
                sp.acquire(); // 当前线程使用count变量的时候将其锁住,不容许其余线程访问
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            try {
                count++;
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(count);
            } catch (RuntimeException e) {
                e.printStackTrace();
            } finally {
                // lock.unlock();
                sp.release(); // 释放锁
            }
        }
    }

3.生产者消费者模型,生成阻塞队列ui

 a.消息通道this

class SemaphoreQueue {
    private List<Integer> valueList;
    private Semaphore putActionNum;// 能够进行put操做的许可数量
    private Semaphore getActionNum;// 能够进行take操做的许可数量
    private Semaphore mutex;       //至关于锁 控制非线程安全的valueList的操做

    public SemaphoreQueue(int capacity) {
        putActionNum = new Semaphore(capacity);// 维护队列大小
        getActionNum = new Semaphore(0);       // 初始化时,队列为空,put操做许可数量为0
        mutex = new Semaphore(1);              // 用于保护非线程安全的valueList操做,用于并发生产时控制
        valueList = new ArrayList<Integer>(capacity);
    }

    public void put(Integer message) {
        try {
            putActionNum.acquire();// put操做许可减1
            mutex.acquire();
            valueList.add(message);
            mutex.release();
            getActionNum.release();// get操做许可加1
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public Integer take() {
        Integer message = null;
        try {
            getActionNum.acquire();// get操做许可减1
            mutex.acquire();
            if (valueList.size() > 0) {
                message = valueList.get(0);
                valueList.remove(0);
            } else {
                return null;
            }
            mutex.release();
            putActionNum.release();// put操做许可加1
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return message;
    }
}

 b. 生产者和消费者spa

class Productor extends Thread {
    SemaphoreQueue queue;
    public Productor(SemaphoreQueue queue) {
        this.queue = queue;
    }
 
    public void run() {
        int i = 0;
        try {
            while (true) {
                i++;
                Integer message = new Integer(i);
                queue.put(message);
                if (i % 20 == 0) {
                    System.out.println("======== " + this.getName() + " 累计生产了 " + i + " 条消息  =======");
                    Thread.currentThread().sleep(1000);
                }

            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Cousumertor extends Thread {
    SemaphoreQueue queue;
    public Cousumertor(SemaphoreQueue queue) {
        this.queue = queue;
    }
    public void run() {
        try {
            while (true) {
                Integer message = queue.take();
                if (message != null) {
                    System.out.println("======== " + this.getName() + " 消费消息:" + message + " =======");
                }
                Thread.currentThread().sleep(100);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 c.测试线程

    public static void main(String[] args) {
        SemaphoreQueue queue = new SemaphoreQueue(20);
        // 开始生产
        Productor productor = new Productor(queue);
        productor.setName("生产者");
        productor.start();
        // 开始消费
        Cousumertor c1 = new Cousumertor(queue);
        c1.setName("消费者-c1");
        Cousumertor c2 = new Cousumertor(queue);
        c2.setName("消费者-c2");
        c1.start();
        c2.start();
    }


4. Semaphore vs. CountDownLatch
   相同点 :
      二者都是用于线程同步的工具类,都经过定义了一个继承AbstractQueuedSynchronizer的内部类Sync来实现具体的功能.
   不一样点 :
  a. Semaphore提供了公平和非公平两种策略, 而CountDownLatch则不具有.
  b. CountDownLatch: 一个或者是一部分线程,等待另一部线程都完成操做。
     Semaphorr: 维护一个许可集.一般用于限制能够访问某些资源(物理或逻辑的)的线程数目. code

  c. CountDownLatch中计数是不能被重置的。CountDownLatch适用于一次同步。当使用CountDownLatch时,任何线程容许屡次调用countDown(). 那些调用了await()方法的线程将被阻塞,直到那些没有被阻塞线程调用countDown()使计数到达0为止 。
  Semaphore容许线程获取许可, 未得到许可的线程须要等待.这样防止了在同一时间有太多的线程执行.Semaphore的值被获取到后是能够释放的,并不像CountDownLatch那样一直减到0。

  d. 使用CountDownLatch时,它关注的一个线程或者多个线程须要在其它在一组线程完成操做以后,在去作一些事情。好比:服务的启动等。使用Semaphore时,它关注的是某一个资源最多同时能被几个线程访问.

相关文章
相关标签/搜索