【Java】----线程同步:生产-消费问题
2016-02-27 15:36
344 查看
一:概念
并发编程:一台处理器上“同时”处理多个任务。线程同步:保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
二:经典模型
1.生产者-消费者一个或多个生产者创建某些工作并将其置于缓冲区或队列中,一个或多个消费者会从队列中获得这些工作并完成
之。这里的缓冲区或队列是临界资源。当缓冲区或队列放满的时候,生产这会被阻塞;而缓冲区或队列为空的时候,
消费者会被阻塞。生产者和消费者的调度是通过二者相互交换信号完成的。
2.读者-写者
当存在一个主要为读者提供信息的共享资源,它偶尔会被写者更新,但是需要考虑系统的吞吐量,又要防止饥饿
和陈旧资源得不到更新的问题。在这种并发模型中,如何平衡读者和写者是最困难的,当然这个问题至今还是一个被
热议的问题,恐怕必须根据具体的场景来提供合适的解决方案而没有那种放之四海而皆准的方法(不像我在国内的科
研文献中看到的那样)。
3.哲学家进餐
五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一盘通心粉。由于通心粉很滑,所以需要两把叉子才能夹
住。相邻两个盘子之间放有一把叉子。哲学家的生活中有两种交替活动时段:即吃饭和思考。当一个哲学家觉得饿了
时,他就试图分两次去取其左边和右边的叉子,每次拿一把,但不分次序。如果成功地得到了两把叉子,就开始吃
饭,吃完后放下叉子继续思考。
三:Java提供线程同步方法:
1.初级:Synchronized关键字:为方法枷锁,保证同一时间只有一个线程可以访问该方法。
Object 类型的wait /notify /notifyall
notify(),notifyAll()都是要唤醒正在等待的线程,前者明确唤醒一个,后者唤醒全部。当程序不明确知道下一
个要唤醒的线程时,需要采用notifyAll()唤醒所有在wait池中的线程,让它们竞争而获取资源的执行权,但使用
notifyAll()时,会出现死锁的风险,因此,如果程序中明确知道下一个要唤醒的线程时,尽可能使用notify()而非
notifyAll()。
消费生产代码:
实例:生产者---消费者:
思路:创建生产者、消费者、产品、盛产品的(先进后出)容器;
为生产者、消费者添加Run和放、取的方法;
<span style="font-family:Verdana;font-size:18px;">import java.lang.Math; public class ProuducerConsumer{ public static void main(String [] args){ SyncStack ss=new SyncStack(); Prouducer p =new Prouducer(ss); Consumer c = new Consumer(ss); new Thread(p).start(); new Thread(c).start(); } } //产品 class Prouduct{ int id; Prouduct(int id){ this.id=id; } public String toString(){ return "Prouduct :"+id; } } //放产品的容器,似堆,先进后出 class SyncStack{ //初始没有产品,初始值为0。 int index =0; //定义容器最多只能放5个。 Prouduct[] arrWT = new Prouduct[5]; //给生产者操作加锁 public synchronized void push(Prouduct pt){ try{ while (index == arrWT.length){ System.out.println("生产满了"); this.wait(); } }catch(InterruptedException e){ e.printStackTrace(); } this.notify(); arrWT[index]= pt; index++; } public synchronized Prouduct pop(){ try{ while(index==0){ System.out.println("消费完了!"); this.wait(); } } catch (InterruptedException e){ e.printStackTrace(); } this.notify(); index--; return arrWT[index]; } } //生产者;实现runnable接口 class Prouducer implements Runnable{ SyncStack ss =null; Prouducer(SyncStack ss){ this.ss=ss; } public void run(){ //最多生产到14就停止生产 for (int i=0;i<15;i++){ Prouduct pt =new Prouduct(i); ss.push(pt); System.out.print("生产:"+pt); try{ //math.random()方法打乱生产、消费的速率。 Thread.sleep((int)(Math.random()*1000)); }catch(InterruptedException e){ e.printStackTrace(); } } } } //消费者 class Consumer implements Runnable{ SyncStack ss =null; Consumer(SyncStack ss){ this.ss=ss; } public void run(){ //最多生产到14就停止生产 for (int i=0;i<15;i++){ Prouduct pt=ss.pop(); System.out.println("消费:"+pt); try{ Thread.sleep((int)(Math.random()*1000)); }catch(InterruptedException e){ e.printStackTrace(); } } } } </span>
如图:
2. 高级:
Java提供了使用抽象层次更高的API。使得线程同步的使用可以更加便利和正确。这些方法在
Java.util.concurrent包中。包括:
1.(Interface )Lock:----代替了Synchronized
方法:Loack()、unlock()、locklnterruptibly()、trylock()。
2.(interface)ReadWriteLock:
ReadWriteLock接口的readLock和writeLock方法来获取对应的锁的Lock接口的实现。
其中:ReentrantLock 实现readlock;
ReentrantReadWriteLock实现writeLock;
3.(interface)Condittion:----代替了wait /nofity
对应方法:await、awaitNanos、awaitUntil。
Signal、signalALL。
四:总结
线程同步的核心是通过互发信号量来协调竞争的临界资源之间的分配来达到同步的。Java在多线程方面做了很多工作,比如容器方面就有一些同步容器和并发容器来适用多线程方面的问问题。通
过这次学习自己稍微了解了点Java多线程的知识,但这还远远不够。像本文例子中适用的同步方法只是Java中很简
单但容错率较低的东西,已经渐渐被淘汰了,但这是基础,很重要。更高层次的还会继续的,等待慢慢探究。
相关文章推荐
- java 内部类学习
- 用Java编写金字塔类型的结构
- spring data jap的openEntityManagerInViewFilter和hibernate的openSessionInViewFilter
- java,二分查找法,网上查阅
- 初学 Java Web 开发,请远离各种框架,从 Servlet 开发
- 在Eclipse中使用JUnit4进行单元测试(高级篇)
- SpringMVC实现poi 解析excel 导入导出
- java,冒泡排序法,网上查阅
- java enum枚举的使用
- JDK自带工具jps,jstat,jmap,jconsole使用
- java基础Junit的使用
- java 算术运算符与自增自减运算符
- Java使用新浪微博API通过账号密码方式登陆微博的实例
- Eclipse中同时打开多个Console
- Eclipse中同时打开多个Console
- Eclipse中同时打开多个Console
- Java parseInt()和parseFloat()的用法
- JAVASE基础 Item -- IO流综合练习
- SpringMVC中使用-sqljdbc4.jar
- Java基础知识(一)