??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
public void actionPerformed(ActionEvent evt) {
displayCanvas.setDone(false);
producer.setDone(false);
startButton.setEnabled(false);
stopButton.setEnabled(true);
feedbackCanvas.setEnabled(true);
feedbackCanvas.requestFocus();
score.resetScore();
}
});
然后又运行了W?章的例子Q发现基本上没有q个问题。难道第3章的代码是正的Q打开源代码一看,重置成W的方法还是放在最后,那这里ؓ什么又是正的呢?我想Q大U是W?章的例子中,每次点击start按钮Q都重新创徏一个线E对象的原因吧。由于创建对象和初始化线E需要一定的旉Q刚好给了主U程重置成W的机会?br />
不知道作者有意ؓ之呢Q还是疏忽,不过Q这L(fng)错误不能是race condition的例子?img src ="http://m.tkk7.com/evanwhj/aggbug/34556.html" width = "1" height = "1" />
]]>
4.1 Wait and Notify
1. {待/唤醒cM于Solaris或POSIX中的条g变量(conditon variables)Q或者Windows中的事g变量(evant variable)或者信号量(signal)Q用于某?多个U程暂停{待某个条g的满I而该条g由其它U程来设|的情况?br />2. 在Java中,像每个对象有一个锁之外QQ何对象都可以提供{待/唤醒的机制。就像Java中的synchronizedL表示获得某个具体对象的锁一Pwait和notify也L{待某个具体的对象,q由该对象唤醒;同样Q获得某个对象上的锁不一定是该对象需要同步一P{待和唤醒的条g也不一定是与之l定的对象?br />3. Java中wait-and-notify的几个方法:
void wait(long timeout, int nanos)Q与wait(long)一P只是在某些JVM中可以精到奈秒?br />void notify(): 唤醒一?/font>正在{待该对象的U程?br />void notifyAll(): 唤醒所?/font>正在{待该对象的U程?
1) sleep()可以在Q何地方调用,而wait()需要在同步Ҏ(gu)或同步块中调用;
2) q入wait()函数ӞJVM会自动释NQ而当从wait()q回卌唤醒Ӟ又会自动获得锁;而sleep()没有q个功能Q因此如果在wait()的地方用sleep()代替Q则会导致相应的nofity()Ҏ(gu)在等待时不可能被触发Q因为notify()必须在相应的同步Ҏ(gu)或同步块中,而此时这个锁却被sleep()所在的Ҏ(gu)占用。也是_wait-and-notify不可能与sleep()同时使用?br />
4.1.1 The Wait-and-Notify Mechanism and Synchronization
1. q一节详l的讲解了wait-and-notify机制和synchronized的关p,主要是两点:1)wait-and-notify必须和synchronized同时使用Q?)wait()会自动释攑֒获取锁;
2. q一节中举了一个例子用来解释可能存在当条g被不满时也有可能被唤醒的情况:
2) T1状态变量,发现其不满条gQ?br /> 3) T1调用wait()Qƈ释放锁;
4) U程T2调用另外一个同步方法,获得锁;
5) U程T3调用另外一个同步方法,׃T2获得了锁Q所以处于等待状态;
6) T2修改状态变量,使其满条gQƈ调用notify()Ҏ(gu)Q?br /> 7) T3获得锁,然后处理数据Qƈ状态变量又讄Z满条g的状态;
8) T3处理完毕q回Q?br /> 9) T1被唤醒,但实际上此时条gq不满? synchronized(obj) {
while(<condition does not hold>)
wait();
... // Perform action appropriate to condition
} synchronized(obj) {
while(<condition does not hold>) {
try {
wait();
} catch (InterruptedException e) {}
}
... // Perform action appropriate to condition
}
4.1.2 wait(), notify(), and notifyAll()
1. 正像多个U程{待同一对象上的锁,当锁释放Ӟ无法定哪个U程会得到那个锁一P当有多个U程在wait()Ӟ当另外一个线E调用nofity()的时候,也不能确定哪个线E会被唤醒; 2. 因此在《Practical Java》的"实践53Q优先用notifyAll()而非notify()"的一Pl合实践54Q可以比较好的解决线E唤醒的问题?br />
4.1.3 Wait-and-Notify Mechanism with Synchronized blocks
再次必须在同一个对象的synchronizedҎ(gu)或块内调用该对象上的wait和notifyҎ(gu)?br />
4.2 Condition Variables
1. 像上面反复的一Pwait-and-notify机制是与特定对象及其上的锁是l定在一L(fng)Q锁和唤醒对象不能分开Q这在某些情况下不是很方便;
2. JDK 1.5提供Condition接口来提供与其它pȝ几乎一致的condition variables机制Q?br />3. Condition对象由Lock对象的newCondition()Ҏ(gu)生成Q从而允怸个锁产生多个条g变量Q可以根据实际情冉|{待不同条gQ?br />4. 该书的例子没有什么特别的实际意义Q但JDK 1.5文档中提供了一个例子,能充分说明用Condition Variables使得E序更加清晰易读Q也更有效率Q?
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
6. 使用Condition Variables的原因:
1) 如果使用Lock对象Q则必须使用condition variablesQ?br /> 2) 每个Lock对象可以创徏多个condition variable.
]]>
3.1 The Synchronized Keywor
1. synchronized是Java中最基本也最常用的用来编写多U程安全代码的关键字Q用以保护对多线E共享的数据的操作L完整的;
2. Atomic: 当一个操作被定义成原子操作时Q意味着该操作在执行q程中不会被打断Q原子操作可以由g保证或者通过软g来模拟;
3. Mutex Lock: 在很多多U程pȝQ通过互斥锁来保护的共享数据。Java中的M一个对象都有一个与之相关的锁,当一个方法被声明成synchronizedӞpC当U程q入该方法前Q必获得相应对象的锁,在执行完毕再释放q个锁。从而保证同一时刻只有一个线E调用该对象上的被声明ؓsynchronized的方法。注意:Java的互斥锁只能加在对象U上Q获得某个对象的锁,q不能保证该对象的属性和其它非synchronized的方法是U程安全的;也不能保证受保护的方法里调用的其它对象是多线E安全的Q除非Q何调用这些没有被保护的方法或者对象只通过受保护的Ҏ(gu)q行调用。所以,~写U程安全的代码关键就在于规划Ҏ(gu)和对象之间的调用关系Qƈ量采用相同对象的锁来进行同步控制?br />
3.2 The Volatile Keyword
1. Scope of a Lock: 锁的作用范围卌得和释放锁之间的那段旉?br /> 2. Java标准虽然声明存取一个非long和double变量的操作是原子操作Q但׃不同虚拟机实现的差异Q在多线E环境下每个U程可能会保留自q工作拯Q而导致变量的g生冲H。ؓ了避免这U情늚发生Q可以有两种Ҏ(gu)Q?br /> 1) 为变量创建声明ؓsynchronized的setter和getterҎ(gu)Q然后Q何调用(包括在类c部Q该变量的地斚w通过setter和getterҎ(gu)Q?br /> 2) 采用volatile声明Q确保每ơ存取这些属性时都从d存中d或者写入主内存中;
3) volatile仅仅用于解决Java内存模式D的问题,只能q用在对该变量只做一个单一装蝲或写入操作且该方法的其它操作q不依赖该变量的变化。如Q在一个@环体中作为递增或递减变量时就不能使用volatile来解决线E同步的问题?br /> 3. volatile的用是有限的,一般而言Q仅仅将其作为强制虚拟机L从主内存d变量的一个手D,或者某些需要其参数声明为volatile的函数?
3.3 More on Race Conditions
本节以打字游戏中昄成W的例子来解释了可能存在的race condition。关键要注意以下几点Q?br /> 1. 操作pȝ会随机的切换多个U程的运行,因此当多个线E调用同一个方法时Q可能在M地方被暂停而将控制权交l另外的U程Q?br /> 2. Z减少被误用的可能QL假设Ҏ(gu)有可能被多个U程调用Q?br /> 3. 锁仅仅与某个特定实例相关Q而与MҎ(gu)和类都无养Iq一点当需要存取类属性或Ҏ(gu)时要特别注意Q?br /> 4. M时候只有一个线E能够运行某个类中一个被声明为synchronized的静态方法;一个线E只能运行某个特定实例中一个被声明为synchronized的非静态方法?br />
3.4 Explicit Locking
1. 学过Win32下编写多U程的朋友刚开始可能会被Java的Synchronized关键词搞p涂。因为Java中的M一个对象都有一个与之相关的锁,而不象在Win32下要先定义一个互斥量Q然后再调用一个函数进入或者离开互斥区域。在JDK 1.5以后也开始提供这U显C声明的锁。JDK 1.5中定义了一个Lock接口和一些类Q允许程序员昄的用锁对象?br />2. 在Lock接口里有两个Ҏ(gu)Qlock()和unlock()用来获取和释N对象Q从而保证受保护的代码区域是U程安全的?br />3. 使用锁对象的一个好处在于可以被保存、传递和抛弃Q以在比较复杂的多线E应用中使用l一的锁?br /> 在用锁对象ӞLlock()和unlock()调用包含在try/finally块中Q以防止q行时异常的抛出而导致死锁的情况?br />
3.5 Lock Scope
利用lock()和unlock()Ҏ(gu)Q我们可以在M地方使用它们Q从一行代码到跨越多个Ҏ(gu)和对象,q样pҎ(gu)E序设计需要来定义锁的作用(scope of lock)范围Q而不是象以前局限在对象的层ơ上?
3.5.1 Synchronized Blocks
1. 利用synchronized关键字也能徏立同步块Q而不一定非得同步整个方法?br /> 2. 同步一D代码时Q需要明的指定获取哪个对象的锁(q就cM于锁对象?Q这P可以在多个Ҏ(gu)或对象中׃nq个锁对象?br />
3.6 Choosing a Locking Mechanism
使用锁对象还是同步关键字(synchronized)Q这个取决于开发员自己。用synchronized比较单,但相对而言Q比较隐晦,在比较复杂的情况?如要同时同步静态和非静态方法时)Q没有锁对象来得直观和统一。一般而言Qsynchronized单,易于使用Q但同时相对而言效率较低Q且不能跨越多个Ҏ(gu)?br />
3.6.1 The Lock Interface
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit)
throws InterruptedException;
void unlock();
Condition newCondition();
}
3.7 Nested Locks
1. 在一个同步方法内调用同一个对象上的另外的同步Ҏ(gu)的情况,UC为嵌套锁(nested locking)。系l将自动执行嵌套的同步方法,而无ȝ待锁的释放。因此,有的时候,即一些私有方法仅仅被已同步的Ҏ(gu)调用Q我们也l其加上synchronized关键字,以减后l维护时可能产生的误对{?br /> 2. ReenterantLockcM支持嵌套锁。在ReenterantLockcȝ持一个计数器Q只有当q个计数器ؓ0Ӟ才会释放锁。注意:q个Ҏ(gu)是ReenterantLockcȝҎ(gu),而不是所有实现Lock接口的类的特性?/strong>
3. 需要支持嵌套锁的一个原因是Ҏ(gu)之间交叉调用(cross-calling)。设惛_象a的方法M1调用对象b的方法N1Q然后N1再调用对象a的方法M2Q而M1和M2都是同步Ҏ(gu)Q如果不支持嵌套锁,则N1在调用M2时等待M1释放锁,而M1则由于N1没有q回永远也不会释NQ这样就产生了死锁?br /> 4. synchronized和Lock接口q没有提供锁对象被嵌套获取的ơ数Q但ReentrantLock则提供了q样一U机Ӟ
public class ReentrantLock implements Lock {
public int getHoldCount();
public boolean isLocked();
public boolean isHeldByCurrentThread();
public int getQueueLength();
...
}
3) isHeldByCurrentThread()判断是否由当前线E获得;
4) getQueueLength()用来估计当前有多线E在{待获取q个锁对象?br />
3.8 Deadlock
介绍了死锁的概念Qƈ修改例子代码来演Cdeadlock的生。死锁一般生在多个U程在多个锁上同步时产生的;当然Q多个线E在判断多个条g的时候,也有可能产生死锁?br />
3.9 Lock Fairness
1. Java里锁的获取机制依赖于底层多线E系l的实现Qƈ不保证一个特定的序Q?br /> 2. ReentrantLockcL供一个先q先?first-in-first-out)的获取锁的选项(在创建锁对象时传入true??img width="1" height="1" src="http://blog.csdn.net/evanwhj/aggbug/616068.aspx" alt="" />
文章来源:http://blog.csdn.net/evanwhj/archive/2006/03/05/616068.aspx
]]>
2.1 What Is a Thread?
介绍了什么是U程Q以及线E?thread, multithread)与进E?process, mutltitask)的区别。其中的一个重要区别是对共享数据的讉K。进E可以共享操作系l别的某些数据区域Q如剪脓(chung)板;而线E是对程序自有的数据q行׃n。进E之间对׃n数据的存取方法是Ҏ(gu)的,因此自然能够得到E序员的注意Q而线E之间对׃n数据的存取与对线E自q数据的存取方法是一L(fng)Q所以常常比较隐蔽,而被E序员忽略。其实,q也是多U程开发比较难的地斏V所以,q一节最后说Q?quot;A thread, then, is a discrete task that operates on data shared with other threads.Q线E就是一个在与其它线E共享的数据上进行操作的单独d。)"
2.2 Creating a Thread
1. 有两U方法创建线E:使用ThreadcL者Runnable接口
2. CZE序竟然是编一个打字游戏,所以,q本书的门槛q是有点高的:(。当然可以从该书的站点直接下?a >代码?
package java.lang;
public class Thread implements Runnable {
public Thread( );
public Thread(Runnable target);
public Thread(ThreadGroup group, Runnable target);
public Thread(String name);
public Thread(ThreadGroup group, String name);
public Thread(Runnable target, String name);
public Thread(ThreadGroup group, Runnable target, String name);
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize);
public void start( );
public void run( );
}
1. Creating a ThreadQ创Z个线E对象,q没有开始执行一个线E?
2. Starting a ThreadQ在调用U程对象的start()Ҏ(gu)前,一直处于等待状态。可以通过调用isAlive()Ҏ(gu)来获取线E是否正在运行;
3. Terminating a ThreadQ线E在以下情况下结束
1)执行完run()中的语句Q?br /> 2)抛出一个异常,包括其没有捕L(fng)异常Q?br /> 3)其所在的E序Q或者容器)调用System.exit()?
注意Q一个线E结束后Q就不能再次启动Q该U程变成一个普通的对象Q如果试囑ֆơ调用start()Ҏ(gu)Q将抛出java.lang.IllegalThreadStateException异常。如果想多次q行Q要么创建新的对象;要么是不要l束该线E?/span>
4. Javaq没有提供可靠的Ҏ(gu)来暂停、挂h者重启一个线E的Ҏ(gu)Q线E本w能通过sleep()函数来暂停执行。目前依然只能确保若q毫U别的_ֺ?
5. Thread CleanupQ线E终止后Q线E对象依然存在。可以通过join()Ҏ(gu)以阻塞方?blocked)来等待某个线E终止?br />
2.4 Two Approches to Stop a Thread
1. Setting a FlagQ在U程的执行过E中判断其它U程是否标志置位。其~点是有旉延迟Q尤其是当进入了某些被阻塞的函数如:sleep(), wait(){;
2. 调用U程的interrupt()Ҏ(gu)Q线E的执行q程中改为判断isInterrupted()的返回倹{可以解决线E进入阻塞导致的延迟Q但依然解决不了长旉计算而导致的延迟。interrupt()有两个作用:1)l止sleep()和wait()Q抛出InterruptedExceptionQ?)使isInterrupted()q回true。下面这D代码将演示interrupt()的作用:
public class TestInterrupted {
public static void main(String args[]) throws InterruptedException{
Foo foo = new Foo();
foo.start();
Thread.currentThread().sleep(100); //注释掉这句有什么媄响呢Q?br /> System.out.println("before interrupt");
foo.interrupt();
System.out.println("after interrupt");
} }
class Foo extends Thread {
public void run() {
try{
while(!isInterrupted()) {
System.out.println("start calculating...");
double pi = 3.1415926;
for(int i=0; i<5; i++) {
for(long j=0; j<1000000; j++) {
pi *= pi;
}
System.out.println("i="+i);
}
System.out.println("before sleep");
sleep(5000); //注释掉这句及相关的try...catch语句Q又怎样呢?
System.out.println("after sleep");
}
} catch(InterruptedException ex) {
ex.printStackTrace(System.out);
}
}
}
2.5 The Runnable Interface
Z么有了Threadc,q要有Runnable接口呢?最主要的原因是Z解决Java不能多重l承的问题。线E承自ThreadQ还是仅仅实现Runnable接口Q取决于q个U程的作用。不q,如果仅仅实现Runnable接口Q则在线E里不能使用ThreadcȝҎ(gu)Q比如interrupt()和isInterrupted()。当Ӟq个q是要取决于实际情况?br />
2.6 Threads and Objects
主要讲解U程与对象的关系。线E根本上q是个对象,其可以存取Q何对象包括其它线E对象,当然也能被其它对象存取,只要没有因ؓ争夺公共资源而被锁定Q线E对象中的Q何属性和Ҏ(gu)都有可能同时执行。ƈ且Java中的锁只针对对象本nQƈ不包括对象下的属性;而对Ҏ(gu)同步Q则{同于对对象同步。更q一步的说明q可以参考《Practical Java》中?quot;实践46Q面对instance函数Qsynchronized锁定的是对象(object)而非函数(methods)或代码(codeQ?quot;。下面用两段代码来说明一下:
代码1:
public class TestLock {
public static void main(String args[])
throws InterruptedException{
Foo foo = new Foo();
foo.start();
Thread t = Thread.currentThread();
for(int i=0; i<10; i++) {
foo.setInt2("Main", i, i+20);
}
}
}
class Foo extends Thread{
protected int arrI[] = new int[10];
public void run() {
try {
for(int i=0; i<10; i++) {
setInt("Foo", i, i);
}
} catch(InterruptedException ex) {}
}
public synchronized void setInt(String from, int pos, int val)
throws InterruptedException{
arrI[pos] = val;
sleep((long)(Math.random()*5000));
System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
}
public void setInt2(String from, int pos, int val)
throws InterruptedException {
synchronized(arrI){
arrI[pos] = val;
sleep((long)(Math.random()*5000));
System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
}
}
}
Foo:arrI[0]=0
Main:arrI[0]=0
Main:arrI[1]=21
Main:arrI[2]=22
Foo:arrI[1]=21
Main:arrI[3]=23
Main:arrI[4]=24
Main:arrI[5]=25
Foo:arrI[2]=2
Main:arrI[6]=26
Main:arrI[7]=27
Foo:arrI[3]=3
Foo:arrI[4]=4
Main:arrI[8]=28
Main:arrI[9]=29
Foo:arrI[5]=5
Foo:arrI[6]=6
Foo:arrI[7]=7
Foo:arrI[8]=8
Foo:arrI[9]=9
public class TestLock1 {
public static void main(String args[])
throws InterruptedException{
Foo1 foo = new Foo1();
foo.start();
Thread t = Thread.currentThread();
for(int i=0; i<10; i++) {
foo.setInt2("Main", i, i+20);
}
}
}
class Foo1 extends Thread{
protected int arrI[] = new int[10];
public void run() {
try{
for(int i=0; i<10; i++) {
setInt("Foo", i, i);
}
}catch(InterruptedException ex){}
}
public synchronized void setInt(String from, int pos, int val)
throws InterruptedException{
arrI[pos] = val;
sleep((long)(Math.random()*5000));
System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
}
public synchronized void setInt2(String from, int pos, int val)
throws InterruptedException{
arrI[pos] = val;
sleep((long)(Math.random()*5000));
System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
}
}