`
pjwqq
  • 浏览: 81468 次
社区版块
存档分类
最新评论

线程同步辅助类semaphore笔记

    博客分类:
  • nio
阅读更多

  Semaphore/信号,用来控制一个或多个共享资源访问。

  例子(Java7并发编程,略做改动):有3台打印机(看作一个资源池),多个客户端请求打印,显然每台打印机一次只能处理一个打印请求。

    1.打印机队列实现

//打印队列,同时支持3台打印机
package java7.lesson3_SemaphoreEx;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import util.Util;

public class PrintQueue {
	private boolean[] freePrinters;
	private Lock lockPrinters;// 该锁用于控制打印机队列的访问
	private final Semaphore semaphore;// 信号量,保护对打印队列的访问
	private final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ");

	public PrintQueue() {
		this.freePrinters = new boolean[3];
		for (int i = 0; i < 3; i++) {
			freePrinters[i] = true;
		}
		this.semaphore = new Semaphore(3);
		lockPrinters = new ReentrantLock();
	}

	public void printJob(Object document) {
			try {
				Util.sop(df.format(new Date())+Thread.currentThread().getName() + ":" + "申请资源...");
				semaphore.acquire();
				int assignedPrinter = getPrinter();
				Util.sop(df.format(new Date())+Thread.currentThread().getName() + ":" + "申请到"+ assignedPrinter + "号打印机,输出打印...");
				Thread.sleep((long) (Math.random() * 5000));
				freePrinters[assignedPrinter] = true;
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				semaphore.release();
			}
	}

	private int getPrinter() {
		int ret = -1;
		try {
			lockPrinters.lock();
			for (int i = 0; i < 3; i++) {
				if (freePrinters[i]) {
					freePrinters[i] = false;
					ret = i;
					break;
				}
			}
		} catch (Exception e) {

		} finally {
			lockPrinters.unlock();
		}
		return ret;
	}

}

   说明:1.信号量semaphore用以控制访问许可,资源池(3个打印机)某时刻只能容纳3个线程访问,其它线程处于阻塞状态,等待获得资源的某个线程释放许可,才能进入。

      2. 线程在阻塞获得许可的这段时间中,可能会被中断,所以acquire()方法会抛出InterruptedException。

           3. lockPrinters用来控制对资源池的访问,getPrinter()需要同步,避免资源使用混乱,这里用一个boolean[]标记。信号量并未锁住任何资源,资源需要另外加以控制。

      4.尤其要注意的是:semaphore是一个相当灵活的使用方式,semaphore.release()之前,并不需要线程先semaphore.acquire(),二者可以不配对出现,A线程获得的许可有可能在B线程释放。换句话说semaphore与线程没有关联。同时灵活性还体现在semaphore不受限于创建时候的初始许可数(这里是3),灵活的代价是我们需要更加注意代码质量吐舌头

  2.模拟打印请求

package java7.lesson3_SemaphoreEx;

public class Job implements Runnable{

	private PrintQueue printQueue; 
	
	public Job(PrintQueue printQueue){
		this.printQueue = printQueue;
	}
	
	@Override
	public void run() {
		printQueue.printJob(new Object());
	}

}

  3.测试

package java7.lesson3_SemaphoreEx;

public class Client {

	public static void main(String[] args) {
		PrintQueue printQueue = new PrintQueue();
		for (int i=0;i<10;i++){
			Job job = new Job(printQueue);
			Thread thread = new Thread(job,i+"号线程");
			thread.start();
		}

	}

}

  测试结果如下:

2014-08-30 12:39:53 0号线程:申请资源...
2014-08-30 12:39:53 0号线程:申请到0号打印机,输出打印...
2014-08-30 12:39:53 2号线程:申请资源...
2014-08-30 12:39:53 2号线程:申请到1号打印机,输出打印...
2014-08-30 12:39:53 3号线程:申请资源...
2014-08-30 12:39:53 4号线程:申请资源...
2014-08-30 12:39:53 4号线程:申请到2号打印机,输出打印...
2014-08-30 12:39:53 6号线程:申请资源...
2014-08-30 12:39:53 8号线程:申请资源...
2014-08-30 12:39:53 5号线程:申请资源...
2014-08-30 12:39:53 1号线程:申请资源...
2014-08-30 12:39:53 7号线程:申请资源...
2014-08-30 12:39:53 9号线程:申请资源...
2014-08-30 12:39:54 3号线程:申请到0号打印机,输出打印...
2014-08-30 12:39:57 6号线程:申请到2号打印机,输出打印...
2014-08-30 12:39:58 8号线程:申请到1号打印机,输出打印...
2014-08-30 12:39:59 5号线程:申请到2号打印机,输出打印...
2014-08-30 12:39:59 1号线程:申请到0号打印机,输出打印...
2014-08-30 12:40:02 7号线程:申请到1号打印机,输出打印...
2014-08-30 12:40:04 9号线程:申请到2号打印机,输出打印...

  输出结果符合预期:前3个线程几乎同时获得申请许可,进入资源池,后面的阻塞直到有空位出现。

 

  另外,还有几点值得了解:

  1.与acquire()的阻塞效果不同,Semaphore提供了 boolean tryAcquire()方法,如果不能获得许可,马上返回false,避免了长时间等待,某些场景比较有用。

  2.Semaphore提供了第2个参数

    public Semaphore(int permits, boolean fair) {
        sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
    }

  在acquire()处阻塞的线程队列中,一旦有新许可出现,让谁先上?如果fair=true, 那就FIFO,如果是false,那就无序了.默认是false,效率比较高一点。(公平锁是如何实现的,大家可以查看源码和大神的讲解,CLH队列锁还是很有意思的,我止步于应用了吐舌头)

 

  和这个例子很相似的一个场景大家应该很熟悉,数据库连接池,连接池中没有空闲资源的时候,线程应该继续等待而不是急匆匆返回失败吧。

 

1
0
分享到:
评论

相关推荐

    MarsNote:记事本Demo,有图文混排,桌面Widget等开发,有线程池和线程同步辅助类的使用

    这款应用集成了图文混排、桌面Widget等实用功能,同时还利用了线程池和线程同步辅助类,以优化性能和提升用户体验。下面将详细解析MarsNote的关键技术点。 1. 图文混排:在MarsNote中,用户可以自由地混合文本和...

    java中的并发变成学习笔记2

    - **CountDownLatch**:它是一个一次性使用的同步辅助类,用于计数。当计数器归零时,所有等待的线程会被释放。在并行计算场景中,CountDownLatch常用来确保所有子任务执行完毕后再进行下一步操作。例如,在`...

    juc尚硅谷-自学笔记

    3. **CountDownLatch**:这是一个同步辅助类,用于控制多个线程的并发访问。计数器从1开始,每当一个线程完成任务时计数器减1,当计数器为0时,所有等待的线程才能继续执行。 4. **CyclicBarrier**:循环栅栏允许一...

    java.util.concurrent 测试源文件

    8. ** Phaser**:Phaser是一个更灵活的同步辅助类,可以替代CyclicBarrier或Semaphore,它允许多个阶段的协调,并且可以动态调整参与者的数量。 在给定的文件中,`pom.xml`可能是Maven项目的配置文件,它包含了项目...

    毕向东 笔记源码

    6. **线程编程**:线程的创建、同步机制(synchronized关键字、wait/notify机制)、并发工具类(ExecutorService、Semaphore、CountDownLatch等)。 7. **反射机制**:如何在运行时获取类的信息,动态创建对象,...

    javase基础学习笔记

    8. **多线程**:创建和管理线程,理解同步机制(synchronized关键字和Lock接口),以及并发工具类如Semaphore、CountDownLatch和CyclicBarrier。 9. **网络编程**:使用Socket进行客户端/服务器通信,理解TCP和UDP...

    jvm-juc:jvm学习笔记

    6. CyclicBarrier和CountDownLatch:同步辅助类,CyclicBarrier用于多线程同步到达某一点后一起开始下一个阶段,而CountDownLatch用于一次性阻塞多个线程,直到计数到零。 7. Semaphore:信号量,用于限制同时访问...

    八股文免费资源下载.zip

    - 线程:线程的创建、同步控制(synchronized、Lock)、死锁预防。 - 高级特性:CountDownLatch、CyclicBarrier、Semaphore的应用。 - Executor框架:ThreadPoolExecutor的配置与使用。 6. **IO流** - 文件操作...

    massive-intense-learningrepo:我用来保存东西的仓库

    5. **多线程**:线程的创建、同步、互斥锁、并发工具类如Semaphore和CountDownLatch等。 6. **Java虚拟机(JVM)**:JVM的工作原理,内存模型,垃圾回收机制,类加载机制等。 7. **设计模式**:工厂模式、单例模式...

    大学计算机专业英语考试.pdf

    7. **信号量(Semaphore)**:一种同步机制,用于多线程和多进程环境中的资源管理和访问控制。 8. **卸载(Uninstaller)**:用于移除软件及其相关文件的程序。 9. **算法(Algorithm)**:解决问题或完成特定任务的...

Global site tag (gtag.js) - Google Analytics