java线程传统VS现代【3】
2016-03-13 14:41
267 查看
12、java5的Semaphere同步工具
Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。
单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。当然由一个线程获得“锁”,然后释放“锁”也行。
厕所,有多少人都能装,线程数动态变化,来一个人产生一个线程
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方法表示。
/ | \
/ | \
-------------------
14、java5的CountDownLatch同步工具
犹如倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。这直接通过代码来说明CountDownLatch的作用,这样学员的理解效果更直接。
15、java5的Exchanger同步工具
用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据。
16、java5阻塞队列的应用
17、java5同步集合类的应用
传统集合类在并发访问时是有问题的,也就是说是线程不安全的。
传统方式下用Collections工具类提供的synchronizedCollection方法来获得同步集合,分析该方法的实现源码。
Collections.synchronizedMap(Map<K,V> m)返回的是一个SynchronizedMap。
这个类是位于Collections中的内部类,其运用了代理模式,给底层的Map加上synchronized。
传统方式下的Collection在迭代集合时,不允许对集合进行修改。
下面这段代码将抛出ConcurrentModificationException异常:
如果删除的是王五,,输出张三,李四后,会抛异常。
所以传统集合中不能用Iterator一边遍历,一边对集合进行操作。
只要把集合从ArrayList改为CopyOnWriteArrayList就能解决这个问题。
Java5中提供了如下一些同步集合类:
通过看java.util.concurrent包下的介绍可以知道有哪些并发集合
ConcurrentHashMap
CopyOnWriteArrayList
CopyOnWriteArraySet
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
相关文章推荐
- java线程传统VS现代【2】
- java队列和栈的使用
- Java内部类的自我理解
- Java并发包中CyclicBarrier的工作原理、使用示例
- 20145221 《Java程序设计》第二周学习总结
- Eclipse/MyEclipse配置SVN插件
- 如何解决eclipse中没有plugin development
- Java遇见HTML——JSP篇之JSP基础语法
- Spring中配置数据源的4种形式
- IT十八掌作业_java基础第九天_多线程、自动拆装箱
- 解决:eclipse中jar包在外面显示
- spring笔记--依赖注入之针对不同类型变量的几种注入方式
- Spring AOP 实现原理与 CGLIB 应用
- 20145216史婧瑶《Java程序设计》第2周学习总结
- 20145216史婧瑶《Java程序设计》第2周学习总结
- eclipse 显示setting目录
- Java多线程
- 解决:eclipse报错:target runtime com.genuitec.runtime.generic.java is not defined
- JDK环境变量设置
- 二、选择排序