---------------------- 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而不能用if
try {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而不能用if
try {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