- 浏览: 386680 次
- 性别:
- 来自: 印度
文章分类
最新评论
-
天天来注册:
...
多线程的死锁 -
memoryisking:
可以看看这篇文章,这个是struts教程网上一个简单的例子,构 ...
Java5中的线程池实例讲解 -
a123159521:
菜鸟不再菜 写道楼主很明显没有说明守护线程到底是怎么服务Use ...
守护线程总结 -
jjruanlili:
要搞个executor和nio的结合,差不多
Java5中的线程池实例讲解 -
josico:
纠正楼主一个问题‘如果四个队员都在忙时,再有新的任务,这个小组 ...
线程池ThreadPoolExecutor使用简介
生产者消费者问题是研究多线程程序时绕不开的问题,它的描述是有一块生产者和消费者共享的有界缓冲区,生产者往缓冲区放入产品,消费者从缓冲区取走产品,这个过程可以无休止的执行,不能因缓冲区满生产者放不进产品而终止,也不能因缓冲区空消费者无产品可取而终止。
解决生产者消费者问题的方法有两种,一种是采用某种机制保持生产者和消费者之间的同步,一种是在生产者和消费者之间建立一个管道。前一种有较高的效率并且可控制性较好,比较常用,后一种由于管道缓冲区不易控制及被传输数据对象不易封装等原因,比较少用。
同步问题的核心在于,CPU是按时间片轮询的方式执行程序,我们无法知道某一个线程是否被执行、是否被抢占、是否结束等,因此生产者完全可能当缓冲区已满的时候还在放入产品,消费者也完全可能当缓冲区为空时还在取出产品。
现在同步问题的解决方法一般是采用信号或者加锁机制,即生产者线程当缓冲区已满时放弃自己的执行权,进入等待状态,并通知消费者线程执行。消费者线程当缓冲区已空时放弃自己的执行权,进入等待状态,并通知生产者线程执行。这样一来就保持了线程的同步,并避免了线程间互相等待而进入死锁状态。
JAVA语言提供了独立于平台的线程机制,保持了”write once, run anywhere”的特色。同时也提供了对同步机制的良好支持。
在JAVA中,一共有四种方法支持同步,其中三个是同步方法,一个是管道方法。
1. 方法wait()/notify()
2. 方法await()/signal()
3. 阻塞队列方法BlockingQueue
4. 管道方法PipedInputStream/PipedOutputStream
下面我们看各个方法的实现:
1. 方法wait()/notify()
wait()和notify()是根类Object的两个方法,也就意味着所有的JAVA类都会具有这个两个方法,为什么会被这样设计呢?我们可以认为所有的对象默认都具有一个锁,虽然我们看不到,也没有办法直接操作,但它是存在的。
wait()方法表示:当缓冲区已满或空时,生产者或消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让另一个线程开始执行;
notify()方法表示:当生产者或消费者对缓冲区放入或取出一个产品时,向另一个线程发出可执行通知,同时放弃锁,使自己处于等待状态。
下面是一个例子代码:
2. 方法await()/signal()
在JDK5.0以后,JAVA提供了新的更加健壮的线程处理机制,包括了同步、锁定、线程池等等,它们可以实现更小粒度上的控制。await()和signal()就是其中用来做同步的两种方法,它们的功能基本上和wait()/notify()相同,完全可以取代它们,但是它们和新引入的锁定机制Lock直接挂钩,具有更大的灵活性。
下面是一个例子代码:
3. 阻塞队列方法BlockingQueue
BlockingQueue也是JDK5.0的一部分,它是一个已经在内部实现了同步的队列,实现方式采用的是我们的第2种await()/signal()方法。它可以在生成对象时指定容量大小。
它用于阻塞操作的是put()和take()方法。
put()方法类似于我们上面的生产者线程,容量最大时,自动阻塞。
take()方法类似于我们上面的消费者线程,容量为0时,自动阻塞。
下面是一个例子代码:
你发现这个例子中的问题了吗?
如果没有,我建议你运行一下这段代码,仔细观察它的输出,是不是有下面这个样子的?为什么会这样呢?
…
warning: it's full!
Producer: java.lang.object@4526e2a
…
你可能会说这是因为put()和System.out.println()之间没有同步造成的,我也这样认为,我也这样认为,但是你把run()中的synchronized前面的注释去掉,重新编译运行,有改观吗?没有。为什么?
这是因为,当缓冲区已满,生产者在put()操作时,put()内部调用了await()方法,放弃了线程的执行,然后消费者线程执行,调用take()方法,take()内部调用了signal()方法,通知生产者线程可以执行,致使在消费者的println()还没运行的情况下生产者的println()先被执行,所以有了上面的输出。run()中的synchronized其实并没有起什么作用。
对于BlockingQueue大家可以放心使用,这可不是它的问题,只是在它和别的对象之间的同步有问题。
对于这种多重嵌套同步的问题,以后再谈吧,欢迎大家讨论啊!
4. 管道方法PipedInputStream/PipedOutputStream
这个类位于java.io包中,是解决同步问题的最简单的办法,一个线程将数据写入管道,另一个线程从管道读取数据,这样便构成了一种生产者/消费者的缓冲区编程模式。
下面是一个例子代码,在这个代码我没有使用Object对象,而是简单的读写字节值,这是因为PipedInputStream/PipedOutputStream不允许传输对象,这是JAVA本身的一个bug,具体的大家可以看sun的解释:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4131126
解决生产者消费者问题的方法有两种,一种是采用某种机制保持生产者和消费者之间的同步,一种是在生产者和消费者之间建立一个管道。前一种有较高的效率并且可控制性较好,比较常用,后一种由于管道缓冲区不易控制及被传输数据对象不易封装等原因,比较少用。
同步问题的核心在于,CPU是按时间片轮询的方式执行程序,我们无法知道某一个线程是否被执行、是否被抢占、是否结束等,因此生产者完全可能当缓冲区已满的时候还在放入产品,消费者也完全可能当缓冲区为空时还在取出产品。
现在同步问题的解决方法一般是采用信号或者加锁机制,即生产者线程当缓冲区已满时放弃自己的执行权,进入等待状态,并通知消费者线程执行。消费者线程当缓冲区已空时放弃自己的执行权,进入等待状态,并通知生产者线程执行。这样一来就保持了线程的同步,并避免了线程间互相等待而进入死锁状态。
JAVA语言提供了独立于平台的线程机制,保持了”write once, run anywhere”的特色。同时也提供了对同步机制的良好支持。
在JAVA中,一共有四种方法支持同步,其中三个是同步方法,一个是管道方法。
1. 方法wait()/notify()
2. 方法await()/signal()
3. 阻塞队列方法BlockingQueue
4. 管道方法PipedInputStream/PipedOutputStream
下面我们看各个方法的实现:
1. 方法wait()/notify()
wait()和notify()是根类Object的两个方法,也就意味着所有的JAVA类都会具有这个两个方法,为什么会被这样设计呢?我们可以认为所有的对象默认都具有一个锁,虽然我们看不到,也没有办法直接操作,但它是存在的。
wait()方法表示:当缓冲区已满或空时,生产者或消费者线程停止自己的执行,放弃锁,使自己处于等待状态,让另一个线程开始执行;
notify()方法表示:当生产者或消费者对缓冲区放入或取出一个产品时,向另一个线程发出可执行通知,同时放弃锁,使自己处于等待状态。
下面是一个例子代码:
import java.util.LinkedList; public class Sycn1...{ private LinkedList<Object> myList =new LinkedList<Object>(); private int MAX = 10; public Sycn1()...{ } public void start()...{ new Producer().start(); new Consumer().start(); } public static void main(String[] args) throws Exception...{ Sycn1 s1 = new Sycn1(); s1.start(); } class Producer extends Thread...{ public void run()...{ while(true)...{ synchronized(myList)...{ try...{ while(myList.size() == MAX)...{ System.out.println("warning: it's full!"); myList.wait(); } Object o = new Object(); if(myList.add(o))...{ System.out.println("Producer: " + o); myList.notify(); } }catch(InterruptedException ie)...{ System.out.println("producer is interrupted!"); } } } } } class Consumer extends Thread...{ public void run()...{ while(true)...{ synchronized(myList)...{ try...{ while(myList.size() == 0)...{ System.out.println("warning: it's empty!"); myList.wait(); } Object o = myList.removeLast(); System.out.println("Consumer: " + o); myList.notify(); }catch(InterruptedException ie)...{ System.out.println("consumer is interrupted!"); } } } } } }
2. 方法await()/signal()
在JDK5.0以后,JAVA提供了新的更加健壮的线程处理机制,包括了同步、锁定、线程池等等,它们可以实现更小粒度上的控制。await()和signal()就是其中用来做同步的两种方法,它们的功能基本上和wait()/notify()相同,完全可以取代它们,但是它们和新引入的锁定机制Lock直接挂钩,具有更大的灵活性。
下面是一个例子代码:
import java.util.LinkedList; import java.util.concurrent.locks.*; public class Sycn2...{ private LinkedList<Object> myList = new LinkedList<Object>(); private int MAX = 10; private final Lock lock = new ReentrantLock(); private final Condition full = lock.newCondition(); private final Condition empty = lock.newCondition(); public Sycn2()...{ } public void start()...{ new Producer().start(); new Consumer().start(); } public static void main(String[] args) throws Exception...{ Sycn2 s2 = new Sycn2(); s2.start(); } class Producer extends Thread...{ public void run()...{ while(true)...{ lock.lock(); try...{ while(myList.size() == MAX)...{ System.out.println("warning: it's full!"); full.await(); } Object o = new Object(); if(myList.add(o))...{ System.out.println("Producer: " + o); empty.signal(); } }catch(InterruptedException ie)...{ System.out.println("producer is interrupted!"); }finally...{ lock.unlock(); } } } } class Consumer extends Thread...{ public void run()...{ while(true)...{ lock.lock(); try...{ while(myList.size() == 0)...{ System.out.println("warning: it's empty!"); empty.await(); } Object o = myList.removeLast(); System.out.println("Consumer: " + o); full.signal(); }catch(InterruptedException ie)...{ System.out.println("consumer is interrupted!"); }finally...{ lock.unlock(); } } } } }
3. 阻塞队列方法BlockingQueue
BlockingQueue也是JDK5.0的一部分,它是一个已经在内部实现了同步的队列,实现方式采用的是我们的第2种await()/signal()方法。它可以在生成对象时指定容量大小。
它用于阻塞操作的是put()和take()方法。
put()方法类似于我们上面的生产者线程,容量最大时,自动阻塞。
take()方法类似于我们上面的消费者线程,容量为0时,自动阻塞。
下面是一个例子代码:
import java.util.concurrent.*; public class Sycn3...{ private LinkedBlockingQueue<Object> queue = new LinkedBlockingQueue<Object>(10); private int MAX = 10; public Sycn3()...{ } public void start()...{ new Producer().start(); new Consumer().start(); } public static void main(String[] args) throws Exception...{ Sycn3 s3 = new Sycn3(); s3.start(); } class Producer extends Thread...{ public void run()...{ while(true)...{ //synchronized(this){ try...{ if(queue.size() == MAX) System.out.println("warning: it's full!"); Object o = new Object(); queue.put(o); System.out.println("Producer: " + o); }catch(InterruptedException e)...{ System.out.println("producer is interrupted!"); } //} } } } class Consumer extends Thread...{ public void run()...{ while(true)...{ //synchronized(this){ try...{ if(queue.size() == 0) System.out.println("warning: it's empty!"); Object o = queue.take(); System.out.println("Consumer: " + o); }catch(InterruptedException e)...{ System.out.println("producer is interrupted!"); } //} } } } }
你发现这个例子中的问题了吗?
如果没有,我建议你运行一下这段代码,仔细观察它的输出,是不是有下面这个样子的?为什么会这样呢?
…
warning: it's full!
Producer: java.lang.object@4526e2a
…
你可能会说这是因为put()和System.out.println()之间没有同步造成的,我也这样认为,我也这样认为,但是你把run()中的synchronized前面的注释去掉,重新编译运行,有改观吗?没有。为什么?
这是因为,当缓冲区已满,生产者在put()操作时,put()内部调用了await()方法,放弃了线程的执行,然后消费者线程执行,调用take()方法,take()内部调用了signal()方法,通知生产者线程可以执行,致使在消费者的println()还没运行的情况下生产者的println()先被执行,所以有了上面的输出。run()中的synchronized其实并没有起什么作用。
对于BlockingQueue大家可以放心使用,这可不是它的问题,只是在它和别的对象之间的同步有问题。
对于这种多重嵌套同步的问题,以后再谈吧,欢迎大家讨论啊!
4. 管道方法PipedInputStream/PipedOutputStream
这个类位于java.io包中,是解决同步问题的最简单的办法,一个线程将数据写入管道,另一个线程从管道读取数据,这样便构成了一种生产者/消费者的缓冲区编程模式。
下面是一个例子代码,在这个代码我没有使用Object对象,而是简单的读写字节值,这是因为PipedInputStream/PipedOutputStream不允许传输对象,这是JAVA本身的一个bug,具体的大家可以看sun的解释:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4131126
import java.io.*; public class Sycn4...{ private PipedOutputStream pos; private PipedInputStream pis; //private ObjectOutputStream oos; //private ObjectInputStream ois; public Sycn4()...{ try...{ pos = new PipedOutputStream(); pis = new PipedInputStream(pos); //oos = new ObjectOutputStream(pos); //ois = new ObjectInputStream(pis); }catch(IOException e)...{ System.out.println(e); } } public void start()...{ new Producer().start(); new Consumer().start(); } public static void main(String[] args) throws Exception...{ Sycn4 s4 = new Sycn4(); s4.start(); } class Producer extends Thread...{ public void run() ...{ try...{ while(true)...{ int b = (int) (Math.random() * 255); System.out.println("Producer: a byte, the value is " + b); pos.write(b); pos.flush(); //Object o = new MyObject(); //oos.writeObject(o); //oos.flush(); //System.out.println("Producer: " + o); } }catch(Exception e)...{ //System.out.println(e); e.printStackTrace(); }finally...{ try...{ pos.close(); pis.close(); //oos.close(); //ois.close(); }catch(IOException e)...{ System.out.println(e); } } } } class Consumer extends Thread...{ public void run()...{ try...{ while(true)...{ int b = pis.read(); System.out.println("Consumer: a byte, the value is " + String.valueOf(b)); //Object o = ois.readObject(); //if(o != null) //System.out.println("Consumer: " + o); } }catch(Exception e)...{ //System.out.println(e); e.printStackTrace(); }finally...{ try...{ pos.close(); pis.close(); //oos.close(); //ois.close(); }catch(IOException e)...{ System.out.println(e); } } } } //class MyObject implements Serializable { //} }
发表评论
-
根据cpu情况决定线程运行数量和情况
2012-07-11 10:24 4026一个线程分配器,根据cpu的负载情况,自动完成对应线程的唤醒或 ... -
线程池shutdown
2011-06-10 14:28 6010写一个程序要求主线程等待子线程运行结束后退出,且子线程同时运行 ... -
利用lock中断线程
2011-01-13 22:53 1369ReentrantLock 的lock机制有2种,忽略中断锁和 ... -
Java5中的线程池实例讲解
2010-12-30 16:23 15690Java5增加了新的类库并发集java.util.concur ... -
线程池ThreadPoolExecutor使用简介
2010-12-30 14:38 38499一、简介 线程池类为 java.util.concurrent ... -
Web应用程序中调度器的启动和关闭问题
2010-12-29 16:30 6880我们知道静态变量是ClassLoader级别的,如果Web应用 ... -
守护线程总结
2010-12-29 11:40 24459在Java中有两类线程:User Thread(用户线程)、D ... -
Java线程:并发协作-生产者消费者模型
2010-08-20 14:37 1062对于多线程程序来说,不管任何编程语言,生产者和消费者模型都 ... -
Java线程:新特征-有返回值的线程
2010-08-20 14:31 1383在Java5之前,线程是没有返回值的,常常为了“有”返回值 ... -
Java线程:新特征-线程池
2010-08-20 14:27 15669Sun在Java5中,对Java线程的类库做了大量的扩展,其中 ... -
Java线程控制权源代码解析
2010-08-20 12:28 1568Java线程控制权的问题十分重要,我们在不断的学习中需要 ... -
详细介绍Java信号量模型实际应用手册
2010-08-20 12:26 1758Java信号量模型需要我们不断的进行学习,在学习的时候会有不 ... -
Java多线程中wait语句的具体使用技巧
2010-08-20 12:24 1864Java多线程在使用的时候 ... -
多线程的死锁
2010-08-19 16:57 2052俗话说,人多好办事!在程序里也是这样,如果是同一个应用程序需要 ... -
线程优先级
2010-07-26 00:15 2184当程序中有多个线程存 ... -
java阻塞队列
2010-07-25 23:14 1346package com.faramita.test.mians ...
相关推荐
在并发编程中,"生产者-消费者"模式是一种经典的解决问题的范式,用于协调两个或更多线程间的协作,其中一部分线程(生产者)生成数据,另一部分线程(消费者)消费这些数据。 生产者-消费者模型的核心在于共享资源...
本示例中的“生产者-消费者”模型是一种经典的多线程问题,它模拟了实际生产环境中的资源分配与消耗过程。下面我们将详细探讨如何在Java中实现这个模型。 首先,我们要理解生产者-消费者模型的基本概念。在这个模型...
java多线程经典模型生产者消费者java多线程经典模型生产者消费者java多线程经典模型生产者消费者java多线程经典模型生产者消费者java多线程经典模型生产者消费者java多线程经典模型生产者消费者java多线程经典模型...
本项目通过一个生产者消费者问题的实例,展示了如何在Java中实现线程间的同步与互斥。 生产者消费者问题是经典的并发问题之一,它涉及到两个类型的线程:生产者和消费者。生产者负责生成数据(产品),而消费者则...
生产者/消费者模型是多线程编程中的一个经典设计模式,它有效地利用了资源,避免了数据竞争和阻塞问题。这个模型的核心思想是将生产者和消费者分隔开,使得生产者可以专注于创建产品,而消费者则专注于消耗这些产品...
java多线程学习,生产者消费者问题示例
总之,Java中的生产者-消费者模式是多线程编程中解决数据共享和同步问题的有效手段,通过合理利用`BlockingQueue`等并发工具类,我们可以构建高效、稳定的多线程应用。在开发过程中,了解和掌握这种模式有助于提高...
在实际应用中,我们常常会遇到一种典型的多线程问题——生产者消费者模型。这个模型描述了两种类型的线程:生产者线程负责创建或生产资源,而消费者线程则负责消耗这些资源。在Java中,我们可以利用同步机制来实现...
生产者消费者模式是一种经典的多线程同步问题解决方案,它源于现实世界中的生产流水线,用于描述生产者(Producer)和消费者(Consumer)之间的协作关系。在这个模式中,生产者负责生成产品并放入仓库,而消费者则从...
Java多线程是一种编程模型,它允许程序同时执行多个任务,从而提高系统效率。在Java中,实现多线程可以通过继承Thread类或者实现Runnable接口来完成。在这个“生产者与消费者”模式中,我们主要关注如何安全地共享...
用java多线程,实现生产者消费者同步和互斥的问题,操作系统中的经典问题
设计目的:通过研究Linux 的进程机制和信号量实现生产者消费者问题的并发控制。说明:有界缓冲区内设有20 个存储单元,放入/取出的数据项设定为1‐20 这20 个整型数。设计要求:1)每个生产者和消费者对有界缓冲区...
java实现多线程经典模型生产者消费java实现多线程经典模型生产者消费java实现多线程经典模型生产者消费java实现多线程经典模型生产者消费java实现多线程经典模型生产者消费java实现多线程经典模型生产者消费java实现...
基本满足操作系统课上要求,java实现的生产者消费者模型。
创建一个简单的生产者消费者模型,可以使用以下伪代码: ```java class Producer implements Runnable { private final BlockingQueue<String> queue; public Producer(BlockingQueue<String> queue) { this....
Java多线程编程是开发高并发、高性能应用的关键技术之一,而生产者消费者模式是多线程编程中常用的一种设计模式。它通过分离数据的生产和消费过程,实现了线程间的协同工作,有效避免了资源的竞争和浪费。在这个模式...
生产者消费者线程问题是多线程编程中的一个经典模型,主要用来解决资源的同步与协作问题。在操作系统课程设计中,这个模型常被用来教授线程间的通信与同步机制,帮助学生理解进程间的协调和资源管理。以下是关于生产...
Java多线程生产者消费者模型是一种典型的线程协作模式,用于解决并发编程中资源的高效利用和同步问题。在这个模型中,"生产者"线程负责生成数据,而"消费者"线程则负责处理这些数据。为了实现这种模式,Java提供了...