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

java线程传统VS现代【3】

2016-03-13 14:41 267 查看
12、java5的Semaphere同步工具

Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。

Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。

单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。当然由一个线程获得“锁”,然后释放“锁”也行。

<span style="font-family:KaiTi_GB2312;font-size:14px;">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(){
<span>	</span>try {
sp.acquire();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3-sp.availablePermits()) + "个并发");
<span>	</span>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);
}
}
}</span>
示例:3个坑 10个人

厕所,有多少人都能装,线程数动态变化,来一个人产生一个线程

ExecutorService service = Exccutors.newCachedThreadPool();

final Semaphore sp = new Semaphore(3);厕所中坑的个数 指定只有3个

3个坑,来了5个人,有2个人要等,其中有一个办完事走了,等待的2个哪个先上呢?默认的构造方法不管,谁抢到了谁上。用new Semaphore(3, true)就可以保证先来的先上。

将坑的个数设置为1就可以达到互斥效果,每次只能有一个线程运行

for (int i=0; i<10; i++)来了10个人

{人的任务 抢坑

Runnable runnable = 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()。


13、java5的CyclicBarrier同步工具

讲解CyclicBarrier的功能时,通过辅助画图的方式说明,效果会更好。

\ /

\ | /

------------------------三个线程干完各自的任务,在不同的时刻到达集合点后,就可以接着忙各自的工作去了,再到达新的集合点,再去忙各自的工作,

到达集合点了用CyclicBarrier对象的await方法表示。

/ | \

/ | \

-------------------

<span style="font-family:KaiTi_GB2312;font-size:14px;">public class CyclicBarrierTest {

public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final  CyclicBarrier cb = new CyclicBarrier(3);
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点1,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();

<span>	</span>Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点2,当前已有" + (cb.getNumberWaiting()+1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();
<span>	</span>Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1) + "个已经到达," + (cb.getNumberWaiting()==2?"都到齐了,继续走啊":"正在等候"));
cb.await();
} catch (Exception e) {
e.printStackTrace();
}}};
service.execute(runnable);
}
service.shutdown();
}
}</span>


14、java5的CountDownLatch同步工具

犹如倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。这直接通过代码来说明CountDownLatch的作用,这样学员的理解效果更直接。

<span style="font-family:KaiTi_GB2312;font-size:14px;">public class CountdownLatchTest {

public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(3);
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
System.out.println("线程" + Thread.currentThread().getName() +
"正准备接受命令");
cdOrder.await();
System.out.println("线程" + Thread.currentThread().getName() +
"已接受命令");
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"回应命令处理结果");
cdAnswer.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread.sleep((long)(Math.random()*10000));

System.out.println("线程" + Thread.currentThread().getName() +
"即将发布命令");
cdOrder.countDown();
System.out.println("线程" + Thread.currentThread().getName() +
"已发送命令,正在等待结果");
cdAnswer.await();
System.out.println("线程" + Thread.currentThread().getName() +
"已收到所有响应结果");
} catch (Exception e) {
e.printStackTrace();
}
service.shutdown();

}
}</span>


15、java5的Exchanger同步工具

用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据。

<span style="font-family:KaiTi_GB2312;font-size:14px;">public class ExchangerTest {

public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Exchanger<String> exchanger = new Exchanger<String>();
service.execute(new Runnable(){
public void run() {
try {

String data1 = "zxx";
System.out.println("线程" + Thread.currentThread().getName() +
"正在把数据" + data1 +"换出去");
Thread.sleep((long)(Math.random()*10000));
String data2 = (String)exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() +
"换回的数据为" + data2);
}catch(Exception e){

}
}
});
service.execute(new Runnable(){
public void run() {
try {

String data1 = "lhm";
System.out.println("线程" + Thread.currentThread().getName() +
"正在把数据" + data1 +"换出去");
Thread.sleep((long)(Math.random()*10000));
String data2 = (String)exchanger.exchange(data1);
System.out.println("线程" + Thread.currentThread().getName() +
"换回的数据为" + data2);
}catch(Exception e){

}
}
});
service.shutdown();
}
}</span>


16、java5阻塞队列的应用

<span style="font-family:KaiTi_GB2312;font-size:14px;">public class BlockingQueueTest {
public static void main(String[] args) {
final BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(3);
for(int i=0;i<2;i++){
new Thread(){
public void run(){
while(true){
try {
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "准备放数据!");
queue.put(1);
System.out.println(Thread.currentThread().getName() + "已经放了数据," +
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}

}.start();
}

new Thread(){
public void run(){
while(true){
try {
//将此处的睡眠时间分别改为100和1000,观察运行结果
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "准备取数据!");
queue.take();
System.out.println(Thread.currentThread().getName() + "已经取走数据," +
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}.start();
}
}</span>


17、java5同步集合类的应用

传统集合类在并发访问时是有问题的,也就是说是线程不安全的。

传统方式下用Collections工具类提供的synchronizedCollection方法来获得同步集合,分析该方法的实现源码。

Collections.synchronizedMap(Map<K,V> m)返回的是一个SynchronizedMap。

这个类是位于Collections中的内部类,其运用了代理模式,给底层的Map加上synchronized。

传统方式下的Collection在迭代集合时,不允许对集合进行修改。

下面这段代码将抛出ConcurrentModificationException异常:

<span style="font-family:KaiTi_GB2312;font-size:14px;">public class CollectionModifyExceptionTest {
public static void main(String[] args) {
Collection users = new ArrayList();//new CopyOnWriteArrayList();
users.add(new User("张三",28));
users.add(new User("李四",25));
users.add(new User("王五",31));
Iterator itrUsers = users.iterator();
while(itrUsers.hasNext()){
System.out.println("aaaa");
User user = (User)itrUsers.next();
if("张三".equals(user.getName())){
users.remove(user);
} else {
System.out.println(user);
}
}
}
}</span>
如果删除的是李四,则不会抛异常,但只会输出张三,王五没有输出。

如果删除的是王五,,输出张三,李四后,会抛异常。

所以传统集合中不能用Iterator一边遍历,一边对集合进行操作。

只要把集合从ArrayList改为CopyOnWriteArrayList就能解决这个问题。

 Java5中提供了如下一些同步集合类:

 通过看java.util.concurrent包下的介绍可以知道有哪些并发集合

 ConcurrentHashMap

 CopyOnWriteArrayList

 CopyOnWriteArraySet
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: