try{
this.wait();
}catch(Exception e){}
}
Food f = (Food)this.removeFirst();
notifyAll();
return f;
}
}
class Chef extends Thread{
Table t;
String name;
Random r = new Random(12345);
public Chef(String name,Table t){
this.t = t;
this.name = name;
}
public void run(){
while(true){
Food f = make();
System.out.println(name+" put a Food:"+f);
t.putFood(f);
}
}
private Food make(){
try{
Thread.sleep(200+r.nextInt(200));
}catch(Exception e){}
return new Food();
}
}
class Eater extends Thread{
Table t;
String name;
Random r = new Random(54321);
public Eater(String name,Table t){
this.t = t;
this.name = name;
}
public void run(){
while(true){
Food f = t.getFood();
System.out.println(name+" get a Food:"+f);
eat(f);
}
}
private void eat(Food f){
try{
Thread.sleep(400+r.nextInt(200));
}catch(Exception e){}
}
}
public class Test {
public static void main(String[] args) throws Exception{
Table t = new Table(10);
new Chef("Chef1",t).start();
new Chef("Chef2",t).start();
new Chef("Chef3",t).start();
new Chef("Chef4",t).start();
new Eater("Eater1",t).start();
new Eater("Eater2",t).start();
new Eater("Eater3",t).start();
new Eater("Eater4",t).start();
new Eater("Eater5",t).start();
new Eater("Eater6",t).start();
}
}
这一个例子中,我们主要关注以下几个方面:
1.同步方法要保护的对象,本例中是保护桌子,不能同时往上放菜或同时取菜。
假如我们把putFood方法和getFood方法在厨师类和食客类中实现,那么我们应该如此:
(以putFood为例)
class Chef extends Thread{
Table t;
String name;
public Chef(String name,Table t){
this.t = t;
this.name = name;
}
public void run(){
while(true){
Food f = make();
System.out.println(name+" put a Food:"+f);
putFood(f);
}
}
private Food make(){
Random r = new Random(200);
try{
Thread.sleep(200+r.nextInt());
}catch(Exception e){}
return new Food();
}
public void putFood(Food f){ //方法本身不能同步,因为它同步的是this.即Chef的实例
synchronized (t) { //要保护的是t
while (t.size() >= t.maxSize) {
try {
t.wait();
}
catch (Exception e) {}
}
t.add(f);
t.notifyAll();
}
}
}
2.同步的范围,在本例中是放和取两个方法,不能把做菜和吃菜这种各自不相干的工作放在受保护的范围中。
3.参与者与容积比
对于生产者和消费者的比例,以及桌子所能放置最多菜的数量三者之间的关系是影响性能的重要因素,如果是过多的生产者在等待,则要增加消费者或减少生产者的数据,反之则增加生产者或减少消费者的数量。
另外如果桌子有足够的容量可以很大程序提升性能,这种情况下可以同时提高生产者和消费者的数量,但足够大的容时往往你要有足够大的物理内存。
多线程编程——实战篇(二)
本节继续上一节的讨论。
[一个线程在进入对象的休息室(调用该对象的wait()方法)后会释放对该对象的锁],基于这个原因。在同步中,除非必要,否则你不应用使用Thread.sleep(long l)方法,因为sleep方法并不释放对象的锁。
这是一个极其恶劣的品德,你自己什么事也不干,进入sleep状态,却抓住竞争对象的监视锁不让其它需要该对象监视锁的线程运行,简单说是极端自私的一种行为。但我看到过很多程序员仍然有在同步方法中调用sleep的代码。
看下面的例子:
package debug;
class SleepTest{
public synchronized void wantSleep(){
try{
Thread.sleep(1000*60);
}catch(Exception e){}
System.out.println("111");
}
public synchronized void say(){
System.out.println("123");
}
}
class T1 extends Thread{
SleepTest st;
public T1(SleepTest st){
this.st = st;
}
public void run(){
st.wantSleep();
}
}
class T2 extends Thread{
SleepTest st;
public T2(SleepTest st){
this.st = st;
}
public void r