------- android培训、java培训、期待与您交流! ----------
例1:问题的引出。(共享数据没有同步) 【误】
class P
{
String name;
String sex;
}
class Producer implements Runnable
{
P q = null;
public Producer(P q)
{
this.q = q;
}
public void run()
{
int i = 0;
while(true)
{
if(i==0)
{
q.name = "Zhang";
q.sex = "mail";
}
else
{
q.name = "LI";
q.sex = "femail";
}
i = (i+1)%2; // 定义了一个切换代码,该表达式的值只能为 1 或 0 。在 if 和 else 之间切换。
}
}
}
class Consumer implements Runnable
{
P q = null;
public Consumer(P q)
{
this.q = q;
}
public void run()
{
while(true)
{
System.out.println(q.name+"---------"+q.sex);
}
}
}
class Test
{
public static void main(String[] args)
{
P k = new P();
Producer i = new Producer(k);
Consumer o = new Consumer(k);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
//new Thread(new Producer(q)).start();
//new Thread(new Consumer(q)).start();
}
}
运行结果: 该程序有问题。 没有同步操作共享数据的代码。可能会出现 name 与 sex 不对应的情况。
例2: 问题的解决。(共享数据同步,加入睡眠唤醒机制,但代码没有优化) 【正】
class P
{
String name;
String sex;
boolean flag = false;
}
class Producer implements Runnable
{
P q = null;
public Producer(P q)
{
this.q = q;
}
public void run()
{
int i = 0;
while(true)
{
synchronized(q) // 锁可以是 Producer.class 、 Consumer.class 、 q 、 Test.class 这些都是唯一的。
{
if(q.flag)
try{q.wait();}catch(Exception e){}
if(i==0)
{
q.name = "Zhang";
q.sex = "mail";
}
else
{
q.name = "LI";
q.sex = "femail";
}
i = (i+1)%2; // 定义了一个切换代码,该表达式的值只能为 1 或 0 。在 if 和 else 之间切换。
q.flag = true;
q.notify();
}
}
}
}
class Consumer implements Runnable
{
P q = null;
public Consumer(P q)
{
this.q = q;
}
public void run()
{
while(true)
{
synchronized(q)
{
if(!q.flag)
try{q.wait();}catch(Exception e){}
System.out.println(q.name+"---------"+q.sex);
q.flag = false;
q.notify();
}
}
}
}
class Test
{
public static void main(String[] args)
{
P k = new P();
Producer i = new Producer(k);
Consumer o = new Consumer(k);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
//new Thread(new Producer(q)).start();
//new Thread(new Consumer(q)).start();
}
}
运行结果: 该程序正常运行,并得出正确结果。对应的两组 name 、 sex 交替出现。
范例分析: 1. wait(). notify(). notifyAll() 都是用在同步中,因为要对持有的监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。
2. 为什么这些操作线程的方法要定义 Object 类中呢
因为这些方法在操作同步中线程时,都必须要标识他们所操作的线程持有的锁,只有同一个锁上的被等待线程,才可以被同一个锁上notify唤醒。不可以
对不同锁中的线程唤醒, 也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object 类中。
例3: 代码优化。(优化代码,但只是用于一个消费者,一个生产者,多个消费者、生产者就不适用了) 【正】
class P
{
private String name;
private String sex;
private boolean flag = false;
public synchronized void set(String name, String sex)
{
if(flag)
try{wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out()
{
if(!flag)
try{wait();}catch(Exception e){}
System.out.println(name+"\t"+sex);
flag = false;
this.notify();
}
}
class Producer implements Runnable
{
P q = null;
public Producer(P q)
{
this.q = q;
}
public void run()
{
int i = 0;
while(true)
{
if(i==0)
{
q.set("zhang","mail");
}
else
{
q.set("LI","femail");
}
i = (i+1)%2; // 定义了一个切换代码,该表达式的值只能为 1 或 0 。在 if 和 else 之间切换。
}
}
}
class Consumer implements Runnable
{
P q = null;
public Consumer(P q)
{
this.q = q;
}
public void run()
{
while(true)
{
q.out();
}
}
}
class Test
{
public static void main(String[] args)
{
P q = new P();
/*
Producer i = new Producer(k);
Consumer o = new Consumer(k);
Thread t1 = new Thread(i);
Thread t2 = new Thread(o);
t1.start();
t2.start();
*/
new Thread(new Producer(q)).start();
new Thread(new Consumer(q)).start();
}
}
运行结果: 该程序正确,并得出正确结果。
范例分析: 优点: 在例2 的基础上采取了优化措施,并封装了代码私有后更简洁更安全。
缺点: 只是用于一个生产者,一个消费者。
例4:延续上个程序的需求,但要加入多个生产者和多个消费者.
class P
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)
{
while(flag) // 此处用 while 循环是为了让判断过并在此等待的线程,在被唤醒时再判断一次,以免在唤醒时被同是生产者或同是消费者给唤醒。
try{wait();}catch(Exception e){}
this.name = name+"....."+count++;
System.out.println(Thread.currentThread().getName()+"-----Producer--------"+count);
flag = true;
this.notifyAll(); // 一个线程在执行完后,为了不唤醒被类中的线程,而让所有线程都被唤醒,让后用 while 再次判断是否该唤醒需要唤醒的线程。
// 当只有一个 Producer 和 一个 Consumer 时, 可以用 notify() ,而当有多个时,用 notifyAll(),来唤醒所有在等待的线程。
} // 如果这里使用 notify ,程序在运行一段时间后自动就停止了,因为没有被唤醒的线程没有执行权,而有执行权的线程却没被唤醒。
public synchronized void out()
{
while(!flag)
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"+++++++++++++Consumer+++++"+count);
flag = false;
this.notifyAll();
}
}
class Producer implements Runnable
{
P q = null;
public Producer(P q)
{
this.q = q;
}
public void run()
{
int i = 0;
while(true)
{
q.set("水果糖");
}
}
}
class Consumer implements Runnable
{
P q = null;
public Consumer(P q)
{
this.q = q;
}
public void run()
{
while(true)
{
q.out();
}
}
}
class Test
{
public static void main(String[] args)
{
P q = new P();
Producer p = new Producer(q);
Consumer c = new Consumer(q);
Thread t1 = new Thread(p); // 定义了四个生产者和四个消费者
Thread t2 = new Thread(p);
Thread t3 = new Thread(c);
Thread t4 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
运行结果: 该程序正确,并得出正确结果。
范例分析 ; 优点: 在例3的基础上让程序可以同时有多个生产者和多个消费者。
缺点: 不适用于一个消费者,一个生产者。