您的位置:首页 > 编程语言 > Java开发

Java 并发专题 : Semaphore 实现 互斥 与 连接池

2015-12-18 11:38 549 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。

继续并发方面的知识。今天介绍Semaphore,同样在java.util.concurrent包下。

本来准备通过例子,从自己实现到最后使用并发工具实现,但是貌似效果并不是很好,有点太啰嗦的感觉,所有准备直入主题。

介绍:Semaphore中管理着一组虚拟的许可,许可的初始数量可通过构造函数来指定【new Semaphore(1);】,执行操作时可以首先获得许可【semaphore.acquire();】,并在使用后释放许可【semaphore.release();】。如果没有许可,那么acquire方法将会一直阻塞直到有许可(或者直到被终端或者操作超时)。

作用:可以用来控制同时访问某个特定资源的操作数量,或者某个操作的数量。

下面使用Semaphore实现两个例子:

1、互斥

大家都学过操作系统,都知道互斥的概念,比较简单的互斥实现,比如PV操作,判断资源,然后忙等实现互斥;上一篇博客也说过,忙等对CPU的消耗巨大,下面我们通过Semaphore来实现一个比较好的互斥操作:

假设我们公司只有一台打印机,我们需要对这台打印机的打印操作进行互斥控制:

[java] view
plaincopy





package com.zhy.concurrency.semaphore;

import java.util.concurrent.Semaphore;

/**

* 使用信号量机制,实现互斥访问打印机

*

* @author zhy

*

*/

public class MutexPrint

{

/**

* 定义初始值为1的信号量

*/

private final Semaphore semaphore = new Semaphore(1);

/**

* 模拟打印操作

* @param str

* @throws InterruptedException

*/

public void print(String str) throws InterruptedException

{

//请求许可

semaphore.acquire();

System.out.println(Thread.currentThread().getName()+" enter ...");

Thread.sleep(1000);

System.out.println(Thread.currentThread().getName() + "正在打印 ..." + str);

System.out.println(Thread.currentThread().getName()+" out ...");

//释放许可

semaphore.release();

}

public static void main(String[] args)

{

final MutexPrint print = new MutexPrint();

/**

* 开启10个线程,抢占打印机

*/

for (int i = 0; i < 10; i++)

{

new Thread()

{

public void run()

{

try

{

print.print("helloworld");

} catch (InterruptedException e)

{

e.printStackTrace();

}

};

}.start();

}

}

}

输出结果:

[java] view
plaincopy





Thread-1 enter ...

Thread-1正在打印 ...helloworld

Thread-1 out ...

Thread-2 enter ...

Thread-2正在打印 ...helloworld

Thread-2 out ...

Thread-0 enter ...

Thread-0正在打印 ...helloworld

Thread-0 out ...

Thread-3 enter ...

Thread-3正在打印 ...helloworld

Thread-3 out ...

通过初始值为1的Semaphore,很好的实现了资源的互斥访问。

2、连接池的模拟实现

在项目中处理高并发时,一般数据库都会使用数据库连接池,假设现在数据库连接池最大连接数为10,当10个连接都分配出去以后,现在有用户继续请求连接,可能的处理:

a、手动抛出异常,用户界面显示,服务器忙,稍后再试

b、阻塞,等待其他连接的释放

从用户体验上来说,更好的选择当然是阻塞,等待其他连接的释放,用户只会觉得稍微慢了一点,并不影响他的操作。下面使用Semaphore模拟实现一个数据库连接池:

[java] view
plaincopy





package com.zhy.concurrency.semaphore;

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.Semaphore;

/**

* 使用Semaphore模拟数据库链接池的使用

* @author zhy

*

*/

public class ConnectPool

{

private final List<Conn> pool = new ArrayList<Conn>(3);

private final Semaphore semaphore = new Semaphore(3);

/**

* 初始化分配3个连接

*/

public ConnectPool()

{

pool.add(new Conn());

pool.add(new Conn());

pool.add(new Conn());

}

/**

* 请求分配连接

* @return

* @throws InterruptedException

*/

public Conn getConn() throws InterruptedException

{

semaphore.acquire();

Conn c = null ;

synchronized (pool)

{

c = pool.remove(0);

}

System.out.println(Thread.currentThread().getName()+" get a conn " + c);

return c ;

}

/**

* 释放连接

* @param c

*/

public void release(Conn c)

{

pool.add(c);

System.out.println(Thread.currentThread().getName()+" release a conn " + c);

semaphore.release();

}

public static void main(String[] args)

{

final ConnectPool pool = new ConnectPool();

/**

* 第一个线程占用1个连接3秒

*/

new Thread()

{

public void run()

{

try

{

Conn c = pool.getConn();

Thread.sleep(3000);

pool.release(c);

} catch (InterruptedException e)

{

e.printStackTrace();

}

};

}.start();

/**

* 开启3个线程请求分配连接

*/

for (int i = 0; i < 3; i++)

{

new Thread()

{

public void run()

{

try

{

Conn c = pool.getConn();

} catch (InterruptedException e)

{

e.printStackTrace();

}

};

}.start();

}

}

private class Conn

{

}

}

[java] view
plaincopy





Thread-0 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651

Thread-2 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@e53108

Thread-1 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@1888759

Thread-0 release a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651

Thread-3 get a conn com.zhy.concurrency.semaphore.ConnectPool$Conn@12b6651

我们测试时,让Thread-0持有一个连接3秒,然后瞬间让3个线程再去请求分配连接,造成Thread-3一直等到Thread-0对连接的释放,然后获得连接。

通过两个例子,基本已经了解了Semaphore的用法,这里的线程池例子只是为了说明Semaphore的用法,真实的实现代码比这复杂的多,而且可能也不会直接用Semaphore。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: