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

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

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

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

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

1、互斥

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

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

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();
} } }

输出结果:

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模拟实现一个数据库连接池:

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
{
} }

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。

好了,之后会继续Java并发的博客。

Java 并发专题 : Semaphore 实现 互斥 与 连接池的更多相关文章

  1. Java并发编程实战 03互斥锁 解决原子性问题

    文章系列 Java并发编程实战 01并发编程的Bug源头 Java并发编程实战 02Java如何解决可见性和有序性问题 摘要 在上一篇文章02Java如何解决可见性和有序性问题当中,我们解决了可见性和 ...

  2. Java并发编程(您不知道的线程池操作), 最受欢迎的 8 位 Java 大师,Java并发包中的同步队列SynchronousQueue实现原理

    Java_并发编程培训 java并发程序设计教程 JUC Exchanger 一.概述 Exchanger 可以在对中对元素进行配对和交换的线程的同步点.每个线程将条目上的某个方法呈现给 exchan ...

  3. Java并发编程(您不知道的线程池操作)

    Java并发编程(您不知道的线程池操作) 这几篇博客,一直在谈线程,设想一下这个场景,如果并发的线程很多,然而每个线程如果执行的时间很多的话,这样的话,就会大量的降低系统的效率.这时候就可以采用线程池 ...

  4. Java并发(二十一):线程池实现原理

    一.总览 线程池类ThreadPoolExecutor的相关类需要先了解: (图片来自:https://javadoop.com/post/java-thread-pool#%E6%80%BB%E8% ...

  5. 【Java 并发】Executor框架机制与线程池配置使用

    [Java 并发]Executor框架机制与线程池配置使用 一,Executor框架Executor框架便是Java 5中引入的,其内部使用了线程池机制,在java.util.cocurrent 包下 ...

  6. java并发编程笔记(七)——线程池

    java并发编程笔记(七)--线程池 new Thread弊端 每次new Thread新建对象,性能差 线程缺乏统一管理,可能无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM 缺 ...

  7. Java并发专题

    ——参考于码农求职小助手公众号 1.并行和并发有什么区别? 1. 并行是指两个或者多个事件在同一时刻发生:而并发是指两个或多个事件在同一时间间隔发生: 2. 并行是在不同实体上的多个事件,并发是在同一 ...

  8. Java 并发专题 :FutureTask 实现预加载数据 在线看电子书、浏览器浏览网页等

    继续并发专题~ FutureTask 有点类似Runnable,都可以通过Thread来启动,不过FutureTask可以返回执行完毕的数据,并且FutureTask的get方法支持阻塞. 由于:Fu ...

  9. Java 并发专题 : CyclicBarrier 打造一个安全的门禁系统

    继续并发专题~ 这次介绍CyclicBarrier:看一眼API的注释: /** * A synchronization aid that allows a set of threads to all ...

随机推荐

  1. Delphi读写UTF-8、Unicode格式文本文件

    // UTF-8文件写入函数procedure SaveUTFFile(const FileName: string; S: string; WriteHeader: Boolean = True); ...

  2. Webbrowser控件execcommand参数详解

    2D-Position 允许通过拖曳移动绝对定位的对象.AbsolutePosition 设定元素的 position 属性为“absolute”(绝对).BackColor 设置或获取当前选中区的背 ...

  3. java学习笔记01--数据类型

    java学习笔记01--数据类型 java数据类型划分 分为两大类型: 1)基本数据类型:类似于普通的值. 2)引用数据类型:传递的是内存的地址. 浮点类型实际上就是表示小数. java基本数据类型 ...

  4. hdu1116--解题报告--初步了解欧拉回路

    由题目意思..我们只要把n个字符串的首尾字母看作是点,这个字符串看着边来处理就可以啦...将题目的案例图形化如下: 那么接着就是欧拉路径和欧拉回路判断,我们这里用并査集来判断图是不是连通的,然后根据有 ...

  5. c语言数组应用--统计随机数并打印直方图

    C标准库中生成伪随机数的是rand函数,使用这个函数需要包含头文件stdlib.h,它没有参数,返回值是一个介于0和RAND_MAX之间的接近均匀分布的整数.RAND_MAX是该头文件中定义的一个常量 ...

  6. IE 兼容模式

    兼容模式的由来 早期的浏览器, IE 独大.这也就导致其很多东西和w3c 的标准有很多不一样的地方.譬如他有自己才看的懂得自定义Tag. 一个熟悉的名词--浏览器大战, 形象的刻画了这期间发生的事情. ...

  7. Python 实现的下载op海贼王网的图片(网络爬虫)

    没得事就爬一下我喜欢的海贼王上的图片 须要在d盘下建立一个imgcache目录 # -*- coding: utf-8 -*- import urllib import urllib2 import ...

  8. 鸟哥Linux私房菜知识点总结6到7章

    近期翻看了一本<鸟哥的Linux私房菜>.这是一本基础的书,万丈高楼平地起.会的不多但能够学.这是我整理的一些知识点.尽管非常基础.希望和大家共同交流. 第6章主机规划与磁盘分区 1.在进 ...

  9. ssh, maven and eclipse 那些破事

    Unix根据该理念keep it simple, keep it stupid.可在j2ee有keep it complex, keep it smart. 所以,我彻底晕菜. 最后能活着把sprin ...

  10. document.write()相关

    原文地址:http://www.cnblogs.com/dh616854836/articles/2140349.html document.write()脚本向窗口(不管是本窗口或其他窗口)写完内容 ...