Java并发库(十四):控制线程访问数量Semaphore
2015-12-25 10:12
495 查看
深切怀念传智播客张孝祥老师,特将其代表作——Java并发库视频研读两遍,受益颇丰,记以后阅
14.java5的Semaphore同步工具
Semaphore可以维护当前访问自身的线程个数,并且提供了同步机制。
semaphore实现的功能类似于厕所里有5个坑,有10个人要上厕所,同时就只能有5个人占用,当5个人中 的任何一个让开后,其中在等待的另外5个人中又有一个可以占用了。
一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore
只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。例如,下面的类使用信号量控制对内容池的访问:
class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = newSemaphore(MAX_AVAILABLE, true);
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
//Not a particularly efficient data structure; just for demo
protected Object[] items = ... whatever kinds of items being managed
protected boolean[] used = new boolean[MAX_AVAILABLE];
protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null; // not reached
}
protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
}
获得一项前,每个线程必须从信号量获取许可,从而保证可以使用该项。该线程结束后,将项返回到池中并将许可返回到该信号量,从而允许其他线程获取该项。注意,调用acquire()时无法保持同步锁,因为这会阻止将项返回到池中。信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。
示例:3个坑 10个人
厕所,有多少人都能装,线程数动态变化,来一个人产生一个线程
ExecutorService service =Exccutors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);厕所中坑的个数 指定只有3个
3个坑,来了5个人,有2个人要等,其中有一个办完事走了,等待的2个哪个先上呢?默认的构造方法不管,谁抢到了谁上。用newSemaphore(3, true)就可以保证先来的先上。
将坑的个数设置为1就可以达到互斥效果,每次只能有一个线程运行
for (int i=0; i<10; i++)来了10个人
{人的任务 抢坑
Runnablerunnable = new Runnable()
{
public void run()
{
sp.acquire();抢坑了 会抛中断异常
}有人占住坑了,给出提示
SOP(currentThreadName+进入,当前已有(3-sp.availablePermits())个人了)
Thread.sleep(5000)蹲坑办事
办完事打声招呼
SOP(ThreadName即将离开)
释放坑的占有权
sp.release();
SOP(ThreadName已经走了,还有sp.availablePermits()个坑可用)
}
开始任务吧
service.execute(runnable)
}
传统互斥只能内部释放锁this.unlock(),进去this.lock()晕倒了别人就没法进去了;用信号灯可以外部释放,其他线程可以释放再获取sp.release() sp.acquire()。
public classSemaphoreTest {
public static void main(String[] args) {
ExecutorService service =Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);
for(int i=0;i<10;i++){
Runnable runnable = newRunnable(){
public voidrun(){
try {
sp.acquire();
} catch(InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("线程" +Thread.currentThread().getName() +
"进入,当前已有" +(3-sp.availablePermits()) + "个并发");
try {
Thread.sleep((long)(Math.random()*10000));
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" +Thread.currentThread().getName() +
"即将离开");
sp.release();
//下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
System.out.println("线程" +Thread.currentThread().getName() +
"已离开,当前已有" +(3-sp.availablePermits()) + "个并发");
}
};
service.execute(runnable);
}
}
}
14.java5的Semaphore同步工具
Semaphore可以维护当前访问自身的线程个数,并且提供了同步机制。
semaphore实现的功能类似于厕所里有5个坑,有10个人要上厕所,同时就只能有5个人占用,当5个人中 的任何一个让开后,其中在等待的另外5个人中又有一个可以占用了。
java.util.concurrent.Semaphore
一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore
只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。例如,下面的类使用信号量控制对内容池的访问:
class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = newSemaphore(MAX_AVAILABLE, true);
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
//Not a particularly efficient data structure; just for demo
protected Object[] items = ... whatever kinds of items being managed
protected boolean[] used = new boolean[MAX_AVAILABLE];
protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null; // not reached
}
protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
}
获得一项前,每个线程必须从信号量获取许可,从而保证可以使用该项。该线程结束后,将项返回到池中并将许可返回到该信号量,从而允许其他线程获取该项。注意,调用acquire()时无法保持同步锁,因为这会阻止将项返回到池中。信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。
构造方法摘要 | ||||
Semaphore(int permits) 创建具有给定的许可数和非公平的公平设置的 Semaphore。 | ||||
Semaphore(int permits, boolean fair) 创建具有给定的许可数和给定的公平设置的 Semaphore。 | ||||
方法摘要 | ||||
void | acquire() 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。 | |||
void | acquire(int permits) 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。 | |||
void | acquireUninterruptibly() 从此信号量中获取许可,在有可用的许可前将其阻塞。 | |||
void | acquireUninterruptibly(int permits) 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。 | |||
int | availablePermits() 返回此信号量中当前可用的许可数。 | |||
int | drainPermits() 获取并返回立即可用的所有许可。 | |||
protected Collection<Thread> | getQueuedThreads() 返回一个 collection,包含可能等待获取的线程。 | |||
int | getQueueLength() 返回正在等待获取的线程的估计数目。 | |||
boolean | hasQueuedThreads() 查询是否有线程正在等待获取。 | |||
boolean | isFair() 如果此信号量的公平设置为 true,则返回 true。 | |||
protected void | reducePermits(int reduction) 根据指定的缩减量减小可用许可的数目。 | |||
void | release() 释放一个许可,将其返回给信号量。 | |||
void | release(int permits) 释放给定数目的许可,将其返回到信号量。 | |||
String | toString() 返回标识此信号量的字符串,以及信号量的状态。 | |||
boolean | tryAcquire() 仅在调用时此信号量存在一个可用许可,才从信号量获取许可。 | |||
boolean | tryAcquire(int permits) 仅在调用时此信号量中有给定数目的许可时,才从此信号量中获取这些许可。 | |||
boolean | tryAcquire(int permits, long timeout,TimeUnit unit) 如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。 | |||
boolean | tryAcquire(long timeout,TimeUnit unit) 如果在给定的等待时间内,此信号量有可用的许可并且当前线程未被中断,则从此信号量获取一个许可。 | |||
厕所,有多少人都能装,线程数动态变化,来一个人产生一个线程
ExecutorService service =Exccutors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);厕所中坑的个数 指定只有3个
3个坑,来了5个人,有2个人要等,其中有一个办完事走了,等待的2个哪个先上呢?默认的构造方法不管,谁抢到了谁上。用newSemaphore(3, true)就可以保证先来的先上。
将坑的个数设置为1就可以达到互斥效果,每次只能有一个线程运行
for (int i=0; i<10; i++)来了10个人
{人的任务 抢坑
Runnablerunnable = new Runnable()
{
public void run()
{
sp.acquire();抢坑了 会抛中断异常
}有人占住坑了,给出提示
SOP(currentThreadName+进入,当前已有(3-sp.availablePermits())个人了)
Thread.sleep(5000)蹲坑办事
办完事打声招呼
SOP(ThreadName即将离开)
释放坑的占有权
sp.release();
SOP(ThreadName已经走了,还有sp.availablePermits()个坑可用)
}
开始任务吧
service.execute(runnable)
}
传统互斥只能内部释放锁this.unlock(),进去this.lock()晕倒了别人就没法进去了;用信号灯可以外部释放,其他线程可以释放再获取sp.release() sp.acquire()。
public classSemaphoreTest {
public static void main(String[] args) {
ExecutorService service =Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);
for(int i=0;i<10;i++){
Runnable runnable = newRunnable(){
public voidrun(){
try {
sp.acquire();
} catch(InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("线程" +Thread.currentThread().getName() +
"进入,当前已有" +(3-sp.availablePermits()) + "个并发");
try {
Thread.sleep((long)(Math.random()*10000));
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程" +Thread.currentThread().getName() +
"即将离开");
sp.release();
//下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
System.out.println("线程" +Thread.currentThread().getName() +
"已离开,当前已有" +(3-sp.availablePermits()) + "个并发");
}
};
service.execute(runnable);
}
}
}
public class SemaphoreTest { public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); final Semaphore sp = new Semaphore(3); for(int i=0;i<10;i++){ Runnable runnable = new Runnable(){ public void run(){ try { sp.acquire(); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3-sp.availablePermits()) + "个并发"); try { Thread.sleep((long)(Math.random()*10000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getName() + "即将离开"); sp.release(); //下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元 System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3-sp.availablePermits()) + "个并发"); } }; service.execute(runnable); } } }
相关文章推荐
- Java并发库(十三):Condition、几个线程有顺序地一个搞一会儿
- Java并发库(十一、十二):线程锁、读写锁
- 从头认识java-15.1 填充容器(1)
- Java并发库(九、十):线程池、Callable、Future
- spring初识2
- Spring MVC介绍
- Java7新特性(二)IO
- Java并发库(八):java5原子性操作类的应用
- spring初识
- MyEclipse中文注释乱码解决方案
- Java并发库(五、六、七):线程范围内共享数据、ThreadLocal、共享数据的三种方法
- Description Resource Path Location Type The prefix “p” for attribute “p:sessionFactory-ref” associat
- JAVA面向对象之多态
- Java并发库(四):传统线程同步通信技术
- Java设计模式_行为型_责任链模式_差旅报销如此简单
- Java enum的用法详解
- jdk 动态代理 数据连接池
- Java线程(二):线程同步synchronized和volatile
- javaWeb Cache技术――OSCache(转-全)
- Java并发库(三):传统线程互斥技术