设为首页 加入收藏

TOP

Java: 使用信号量(Semaphore)保护多个共享资源的访问
2015-07-16 12:57:21 来源: 作者: 【 】 浏览:7
Tags:Java: 使用 信号 Semaphore 保护 多个 共享资源 访问

信号量(semaphore)机制是一种常用的同步机制,在现代OS中被广泛采用。semaphore是一个非0值,当它的值大于0时表示系统目前还有足够的资源分配给请求线程,每分配出去一个资源,值递减。当值等于0时表示当前已无资源可分配。JDK提供了Semaphore类来实现信号量。


假如我们一共有3台打印机可用,当前有N个线程都请求使用打印机,要实现对打印机这种资源的访问保护,有以下两种方式:


显然,方式一在某个时间点只能有一个线程在使用打印机,那么另外2台打印机资源就被浪费掉了。有人可能会想,如果只对检查并分配打印机的代码进行加锁,而在使用打印机的过程中不加锁,这样当第一个线程使用打印机时,第二个线程依然能够请求到打印机,不就能允许3个线程都用上打印机了吗?


这样做是非常危险的,我们称之为资源逃逸。如果不对使用打印机的过程加锁,那么如果请求线程又将这台逃逸出的打印机分配给了其它线程,那么线程同步就失去了意义。


对于方式二,当线程A使用打印机时,线程B依然可以请求并使用打印机,线程C也可以。当线程A使用完后必须释放semaphore, 这样其它线程又可以使用这台打印机了。


首先定义打印机对象:


class Printer {
? ? private String name;
? ? private int index;


? ? public Printer(String name, int index) {
? ? ? ? this.name = name;
? ? ? ? this.index = index;
? ? }


? ? public void print() {
? ? ? ? System.out.println("printer " + name + " is working for " + Thread.currentThread().getName());


? ? ? ? try {
? ? ? ? ? ? Thread.currentThread().sleep(10);
? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }


? ? ? ? System.out.println("printer " + name + " for " + Thread.currentThread().getName() + " is done");
? ? }


? ? public int getIndex() {
? ? ? ? return this.index;
? ? }
}


然后,我们定义一个打印机池(Pool)。利用这个pool实现打印机资源的安全分配:


class PrinterPool {
? ? /**
? ? * store all available devices
? ? */
? ? private Printer[] printers;
? ? private boolean[] freePrinters;


? ? private Lock lock = new ReentrantLock();


? ? /**
? ? * create a semaphore for 3 resources
? ? */
? ? private Semaphore semaphore = new Semaphore(3);


? ? /**
? ? * 3 printers available in this pool.
? ? */
? ? public PrinterPool() {
? ? ? ? printers = new Printer[] {
? ? ? ? ? ? ? ? new Printer("A", 0),
? ? ? ? ? ? ? ? new Printer("B", 1),
? ? ? ? ? ? ? ? new Printer("C", 2)
? ? ? ? };


? ? ? ? freePrinters = new boolean[] {
? ? ? ? ? ? ? ? true,
? ? ? ? ? ? ? ? true,
? ? ? ? ? ? ? ? true
? ? ? ? };
? ? }


? ? /**
? ? * use printer
? ? */
? ? public Printer printData() {
? ? ? ? Printer printer = null;


? ? ? ? try {
? ? ? ? ? ? semaphore.acquire();


? ? ? ? ? ? while (true) {
? ? ? ? ? ? ? ? printer = getFreePrinter();


? ? ? ? ? ? ? ? if (printer != null) {
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }


? ? ? ? ? ? // use printer
? ? ? ? ? ? printer.print();


? ? ? ? ? ? // reclaim printer
? ? ? ? ? ? reclaimPrinter(printer);


? ? ? ? } catch (InterruptedException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? } finally {
? ? ? ? ? ? semaphore.release();
? ? ? ? }


? ? ? ? return printer;
? ? }


? ? private Printer getFreePrinter() {
? ? ? ? Printer printer = null;


? ? ? ? lock.lock();
? ? ? ? for (int ix = 0 ; ix < freePrinters.length ; ++ix) {
? ? ? ? ? ? if (true == freePrinters[ix]) {
? ? ? ? ? ? ? ? freePrinters[ix] = false;
? ? ? ? ? ? ? ? printer = printers[ix];
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? lock.unlock();


? ? ? ? return printer;
? ? }


? ? private void reclaimPrinter(Printer p) {
? ? ? ? lock.lock();
? ? ? ? this.freePrinters[p.getIndex()] = true;
? ? ? ? lock.unlock();
? ? }


}


最后编写测试代码,启动10个线程请求使用打印机:


// create printer pool
? ? ? ? PrinterPool pool = new PrinterPool();


? ? ? ? Runnable job = () -> {
? ? ? ? ? ? pool.printData();
? ? ? ? };


? ? ? ? // create 10 threads
? ? ? ? Thread[] threads = new Thread[10];
? ? ? ? for (int ix = 0 ; ix < 10 ; ++ix) {
? ? ? ? ? ? threads[ix] = new Thread(job);
? ? ? ? }


? ? ? ? // start all threads
? ? ? ? for (int ix = 0 ; ix < 10 ; ++ix) {
? ? ? ? ? ? threads[ix].start();
? ? ? ? }


输出结果:


这里写图片描述


】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇使用shell进行数学运算 下一篇C++中动态申请数组

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: