首先,在java jdk1.5之前实现异步安全是通过关键字synchronized将对象和方法或者代码块加锁来实现的,
在生产者消费者的案例中,可以利用此方法来完成生产者生产一个消费者消费一个的控制,但有一个弊端就是, notifyAll解决了本方线程一定会唤醒对方线程的问题,但本方的线程也被唤醒了,这虽然对结果没有影响,却大大降低了效率。下面先给出通过关键字synchronized的实现:
<span style="font-size:18px;">package Test;
/*
生产者,消费者。
多生产者,多消费者的问题。
if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行!
notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。
*/
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name) {
while (flag)
// if判断标记,只有一次,会导致不该运行的线程运行了。出现数据错误的情况,有的烤鸭没有被消费
try {
this.wait();
} catch (InterruptedException e) {
}
this.name = name + count;// 烤鸭1 烤鸭2 烤鸭3
count++;// 2 3 4
System.out.println(Thread.currentThread().getName() + "...生产者..."
+ this.name);// 生产烤鸭1 生产烤鸭2 生产烤鸭3
flag = true;
notifyAll();
}
public synchronized void out() {
while (!flag)
// if判断标记,只有一次,会导致不该运行的线程运行了。出现数据错误的情况,有的会被消费多次
try {
this.wait();
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "...消费者........"
+ this.name);// 消费烤鸭1
flag = false;
notifyAll();// 唤醒所有等待的线程 notify()为随机唤醒一个线程
}
}
class Producer implements Runnable {
private Resource r;
Producer(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.set("烤鸭");
}
}
}
class Consumer implements Runnable {
private Resource r;
Consumer(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.out();
}
}
}
class ProducerConsumerDemo {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
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();
}
}
</span>
jdk1.5以后将同步和锁封装成了对象。 并将操作锁的隐式方式定义到了该对象中, 将隐式动作变成了显示动作。
1. Lock接口: 替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。同时更为灵活。可以一个锁 上加上多组监视器(Condition)。
lock():获取锁。
unlock():释放锁,通常需要定义finally代码块中。
2. Condition接口:出现替代了Object中的wait notify notifyAll方法。将这些监视器方法单独进行了封装,变成ondition监视器对象。可以任意锁进行组合。对象方法有 await(),signal(),signalAll(),分别对用以前的wait(),notify(),notifyAll()方法。
具体代码(包含和上面代码的对比)如下:
<span style="font-size:18px;">package Test;
import java.util.concurrent.locks.*;
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
// 创建一个锁对象。
Lock lock = new ReentrantLock();
// 通过已有的锁获取该锁上的监视器对象。
// Condition con = lock.newCondition();
// 通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
Condition producer_con = lock.newCondition();
Condition consumer_con = lock.newCondition();
public void set(String name) {
lock.lock();
try {
while (flag)
// try{lock.wait();}catch(InterruptedException e){}
try {
producer_con.await();
} catch (InterruptedException e) {
}// t1 t0
this.name = name + count;// 烤鸭1 烤鸭2 烤鸭3
count++;// 2 3 4
System.out.println(Thread.currentThread().getName()
+ "...生产者5.0..." + this.name);// 生产烤鸭1 生产烤鸭2 生产烤鸭3
flag = true;
// notifyAll();
// con.signalAll();
consumer_con.signal();
} finally {
lock.unlock();
}
}
public void out()// t2 t3
{
lock.lock();
try {
while (!flag)
// try{this.wait();}catch(InterruptedException e){} //t2 t3
try {
consumer_con.await();
} catch (InterruptedException e) {
} // t2 t3
System.out.println(Thread.currentThread().getName()
+ "...消费者.5.0......." + this.name);// 消费烤鸭1
flag = false;
// notifyAll();
// con.signalAll();//signalAll()也会唤醒本方线程
producer_con.signal();
} finally {
lock.unlock();// 这个代码块很重要,发生了异常也要保证解锁
}
}
}
class Producer implements Runnable {
private Resource r;
Producer(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.set("烤鸭");
}
}
}
class Consumer implements Runnable {
private Resource r;
Consumer(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.out();
}
}
}
class ProducerConsumerDemo2 {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
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();
}
}
</span>
下面说一下wait 和 sleep 区别:
1,wait可以指定时间也可以不指定。
sleep必须指定时间。
2,在同步中时,对cpu的执行权和锁的处理不同。
wait:释放执行权,释放锁。否则将不能够被唤醒。
sleep:释放执行权,不释放锁。自己sleep完了自己会醒