黑马程序员_多线程2

来源:互联网 发布:滨州北海大数据产业园 编辑:程序博客网 时间:2024/05/18 02:48

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

       通过上一节的学习,对多线程有了较为全面的了解,这里记录的主要是多线程的应用,以及JDK1.5以后的变化。

      一、线程间通信:假设一个储存空间有两部分,一部分储存认得姓名,另一部分储存认得性别。有两个线程,一个向该存储空间添加数据(生产者),另一个取走数据(消费者)。这里需要考虑两个问题,1),假设生产着线程刚添加完姓名,还没有添加性别,CPU就切换到消费者线程了,出现数据不同步,引起不安全因素;2),生产者放入了N个数据,消费者才取一次一次数据,或者消费者重复取N个数据,也出现了安全问题。

1,解决数据不同步的问题,将添加和取走的语句放到同一个同步锁中;

2,解决只存不取或只取不存的问题,用等待唤醒机制,wait,notify,notifyAll,

wait();让线程处于冻结状态,被wait的线程被存储到线程池中,

notify();唤醒线程池中的一个线程(任意),

notifyAll();唤醒线程池中的所有线程。

      多生产者多消费者问题:
if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行!

notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。

package itcast;public class ProducerConsumerDemo {public static void main(String[] args) {Resource res = new Resource();Producer pro = new Producer(res);Consumer con = new Consumer(res);Thread t0 = new Thread(pro);Thread t1 = new Thread(pro);Thread t2 = new Thread(con);Thread t3 = new Thread(con);t0.start();t1.start();t2.start();t3.start();}}class Resource{String name;boolean flag = false;int count = 1;public synchronized void set(String name){while(flag)//此处要用while而不能用iftry {wait();} catch (InterruptedException e) {}this.name = name+count;count++;System.out.println(Thread.currentThread().getName()+"....生产了...."+this.name);flag = true;notifyAll();//不能用notify,以防唤醒己方生产者线程带来不安全因素}public synchronized void get(){while(!flag)try {wait();} catch (InterruptedException e) {}System.out.println(Thread.currentThread().getName()+".........消费了...."+name);flag = false;notifyAll();}}class Producer implements Runnable{private Resource res;Producer(Resource res) {this.res = res;}public void run() {while(true){res.set("烤鸭");}}}class Consumer implements Runnable{private Resource res;Consumer(Resource res) {this.res = res;}public void run() {while(true){res.get();}}}

       二、JDK1.5以后的变化

我们知道,notify唤醒的线程是随机的,如上面的例子,不能确定是唤醒的消费者线程还是生产者线程,1.5以后的变化就可以解决这个问题

jdk1.5以后将同步和锁封装成了对象, 并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。

Lock接口: 出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。同时更为灵活。可以一个锁上加上多组监视器。
主要方法介绍:lock():获取锁;unlock():释放锁,通常需要定义finally代码块中,因为获取锁以后不管是否发生异常都要释放锁。

 Condition接口:出现替代了Object中的wait notify notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象。可以任意锁进行组合。
主要方法:await()相当于wait();signal()相当于notify();signalAll()相当于notifyAll();

这样我们可以再一个锁Lock上定义两个监视器Condition,分别监视生产者和消费者。更改后的代码如下:

import java.util.concurrent.locks.*;public class ProducerConsumerDemo2 {public static void main(String[] args) {Resource res = new Resource();Producer pro = new Producer(res);Consumer con = new Consumer(res);Thread t0 = new Thread(pro);Thread t1 = new Thread(pro);Thread t2 = new Thread(con);Thread t3 = new Thread(con);t0.start();t1.start();t2.start();t3.start();}}class Resource{String name;boolean flag = false;int count = 1;Lock lock = new ReentrantLock();//创建锁对象//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者Condition proCondition = lock.newCondition();Condition conCondition = lock.newCondition();public  void set(String name){lock.lock();try{while(flag)//此处要用while而不能用iftry {proCondition.await();} catch (InterruptedException e) {}this.name = name+count;count++;System.out.println(Thread.currentThread().getName()+"....生产了...."+this.name);flag = true;conCondition.signal();}finally{lock.unlock();}}public  void get(){lock.lock();try{while(!flag)try {conCondition.await();} catch (InterruptedException e) {}System.out.println(Thread.currentThread().getName()+".........消费了...."+name);flag = false;proCondition.signal();}finally{lock.unlock();}}}class Producer implements Runnable{private Resource res;Producer(Resource res) {this.res = res;}public void run() {while(true){res.set("烤鸭");}}}class Consumer implements Runnable{private Resource res;Consumer(Resource res) {this.res = res;}public void run() {while(true){res.get();}}}

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net

 

原创粉丝点击