java.util.concurrent.Semaphore 使用
1. 概述
Semaphore(信号) 并不存在真正的许可 只是维护一个计数器, 通常用来限定进入一些资源的线程数
accquire() 方法获取许可 成功则计数器值-1 没有则阻塞直到一个可用的许可(即计数器>0)
release() 方法 潜在的释放了申请人(通过给计数器值+1)
2. 示例一(单独测试信号量增减 availabelPermits对超出数量的线程的阻塞)
package com.rocky.semaphore; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class SemaphoreTest { public static void main(String[] args) { Semaphore semaphore = new Semaphore(5, false);//no fair 并行最大为5,阻塞后来的 ExecutorService service = Executors.newCachedThreadPool(); for(int i=0; i<10; i++){ service.execute(new Worker(semaphore)); } service.shutdown(); } } class Worker implements Runnable{ private Semaphore semaphore; Worker(Semaphore semaphore){ this.semaphore = semaphore; } @Override public void run() { try { semaphore.acquire();//获取许可 try { System.out.println(Thread.currentThread().getName()+" accessing..."); Thread.sleep((long) (Math.random()*3000)); } finally{ semaphore.release();//释放许可 System.out.println(Thread.currentThread().getName()+" leaving..."); } } catch (InterruptedException e) { e.printStackTrace(); } }}
说明: 默认是非公平的 进来一个线程获取许可, 则state减1,直到值为0 以下是源码片段
final int nonfairTryAcquireShared(int acquires) {//acquires值为1 for (;;) { int available = getState();//当前state值 int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining))//remaining>0 则CAS修改state 成功获取许可 return remaining; } } //remainimg<0 返回后,执行下面方法 private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED);//创建共享型节点加入等待队列 队列为空则仿制头结点并建立联系 try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg);//再次尝试获取许可 if (r >= 0) {//成功获取许可 setHeadAndPropagate(node, r); p.next = null; // help GC return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) break; } } catch (RuntimeException ex) { cancelAcquire(node); throw ex; } // Arrive here only if interrupted cancelAcquire(node); throw new InterruptedException(); }
3. 示例二(一个生产者与一个消费者 两组信号量 此消彼长 为0阻塞)
package com.rocky.semaphore; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class ProducerCustomRealizeWithSemaphore { public static void main(String[] args) { SemaphoreBuffer semaphoreBuffer = new SemaphoreBuffer(); Producer producer = new Producer(semaphoreBuffer); Customer customer = new Customer(semaphoreBuffer); ExecutorService service = Executors.newCachedThreadPool(); service.execute(customer); service.execute(producer); service.shutdown(); } } class SemaphoreBuffer{ List<Integer> list = new ArrayList<Integer>(); Semaphore producerSemaphore = new Semaphore(1);// 允许并行的线程数为1 Semaphore customerSemaphore = new Semaphore(0);// 0即state的初始值 则一开始消费就阻塞了 见上例说明中remaining<0 public void put(int num){ try { producerSemaphore.acquire(); try { list.add(num); } finally{ customerSemaphore.release(); } } catch (InterruptedException e) { e.printStackTrace(); } } public int get(){ try { customerSemaphore.acquire(); try{ return list.remove(0); }finally{ producerSemaphore.release(); } } catch (InterruptedException e) { e.printStackTrace(); } return 0; } } class Customer implements Runnable{ private SemaphoreBuffer buffer; Customer(SemaphoreBuffer buffer){ this.buffer = buffer; } @Override public void run() { while(!Thread.interrupted()){ int num = buffer.get(); System.out.println("Customer get the num "+num); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Producer implements Runnable{ private SemaphoreBuffer buffer; Producer(SemaphoreBuffer buffer){ this.buffer = buffer; } int c =0; @Override public void run() { while(!Thread.interrupted()){ buffer.put(c); System.out.println("Producer put the num "+c); c++; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
4. 示例三(多个生产者与多个消费者 运用阻塞队列 线程安全 )
package com.rocky.semaphore; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; public class ProducerCustomRealizeWithSemaphoreLinkedBlockingQueue { static AtomicInteger c = new AtomicInteger(1); public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); CakeStand stand = new CakeStand(); service.execute(new CakeProducer(stand, "producer1", c)); service.execute(new CakeProducer(stand, "producer2", c)); service.execute(new CakeProducer(stand, "producer3", c)); service.execute(new CakeCustomer(stand, "customer1")); service.execute(new CakeCustomer(stand, "customer2")); } } class Cake{ private String name; Cake(String name){ this.name = name; } public String toString(){ return name; } } class CakeStand{ BlockingQueue<Cake> queue = new LinkedBlockingQueue<Cake>(15); Semaphore notFull = new Semaphore(10);//生产信号量 Semaphore notEmpty = new Semaphore(0);//消费信号量 public void put(Cake cake){ try { notFull.acquire(); try{ queue.put(cake); }finally{ notEmpty.release(); } } catch (InterruptedException e) { e.printStackTrace(); } } public Cake take(){ try { notEmpty.acquire(); try{ Cake cake = queue.take(); return cake; }finally{ notFull.release(); } } catch (InterruptedException e) { e.printStackTrace(); } return null; } } class CakeProducer implements Runnable{ private CakeStand stand; private String name; private AtomicInteger c; public CakeProducer(CakeStand stand, String name, AtomicInteger c) { this.stand = stand; this.name = name; this.c = c; } @Override public void run() { while(!Thread.interrupted()){ String str = "cake-"+c.getAndIncrement(); System.out.println("生产:"+name+"-"+str); stand.put(new Cake(str)); try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } } } } class CakeCustomer implements Runnable{ private CakeStand stand; private String name; public CakeCustomer(CakeStand stand, String name){ this.stand = stand; this.name = name; } @Override public void run() { while(!Thread.interrupted()){ Cake cake = stand.take(); System.err.println("消费:"+name+"-"+cake.toString()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
说明1): 传统的消费者 生产者使用wait/notify模式等待和相互唤醒, Semaphore通过信号量的值控制运行(>0)和阻塞(<=0),
两组信号量可以使两组角色彼此唤醒,使用阻塞队列可以确保线程安全。
2) 可以额外创建一个信号量Semaphore mutex = new Semaphore(1); 在获取本组信号量之后再获取metex信号量可以实现互斥锁效果