每个对象都有一个机锁(lock,也成为monitor),它是对象与生具有的一部分(无需撰写特定程序代码即可拥有)。当调用任何synchronized函数时,对象变被锁定,该对象的所有synchronized函数便无法再被调用,直到第一个函数执行完毕并解除锁定为止。同一个对象的所有synchronized函数共用同一个机锁,而此机锁可防止一个以上的函数同时对同一块内存进行改写动作。
每个class也有一个独立机锁(是class所对应的Class对象的一部分)。所以,synchronized static函数可以在“影响及于整个class”的效力下进行锁定,避免static数据被同时访问。
Java通过所谓的同步区段支持关键区。关键字synchronized被用来指定“以某个对象的机锁”对“被大括号括起来的程序代码”实施同步控制。
线程可能处于以下四种状态之一:
- 新生(New):线程对象已诞生,但尚未被启动,所以无法执行。
- 可执行(Runnable):意指在分时多工机制下,当系统有能力拨出CPU执行时间给予线程,线程便可执行。这种状态下的线程可能正在执行,也可能不处于执行状态。不执行不同于死亡或停滞。
- 死亡(Dead):线程的正常结束方式,便是从run()返回。也可以调用stop(),但这个动作会抛出异常。因此Java2不赞成使用stop(),也不赞成使用destroy(),因为它不会释放对象的机锁。
- 停滞(Blocked):线程可以执行,但是存在某个阻碍因素。当线程处于停滞状态,系统排程器会略过它,不给予任何CPU时间。直到线程重回可执行状态,才有可能被执行。
基于五个理由,线程可能会变成停滞状态:
- 通过sleep(millisecons)让线程进入休眠状态。这种情况下在指定期间内它无法醒来执行。
- 通过suspend()暂停线程。除非该线程收到resume()消息,否则不会变回可执行状态。Java2已不赞成使用此法,因为suspend不会释放机锁,这样容易导致死锁。
- 通过wait()暂停线程。除非该线程收到notify()或nofityAll()消息,否则不会变回可执行状态。如果使用wait(),它被调用时会释放机锁。只能对拥有的机锁调用wait()或notify()。
- 线程正在等待某个I/O动作完成,它便会自动进入停滞状态。
- 线程试着调用另外一个对象的synchronized函数,而且尚未取得该对象的机锁。
也可以调用yield(),自愿放弃CPU执行权利,让其它线程有机会执行。如果排程器认为当前线程已经执行够长一段时间,应该换另一个线程执行,也会发生停滞情况。也就是说没有任何力量可以阻止排程器“取走你的线程的执行权,并将CPU时间交给其它线程”。当某个线程处于停滞状态,表示存在某种让线程无法执行的原因。
wait()和notify()有个特性:两个函数都是基类Object的一部分,不像sleep()、suspend()、resume()一样属于Thread。乍看之下有点奇怪,Object竟然拥有一些专门用于线程,而非基类所应有的、普遍性的东西,但这是必要的,因为这两个函数会取用对象机锁,而机锁正是每个对象都会拥有的。如此一来,不论在某个class中是否正有某个线程正在执行,你都可以将wait()置于任何synchronized函数中。事实上,也只能在synchronized函数或同步区段中调用wait()。sleep()、suspend()、resume()都可在non-synchronized函数中被调用,因为那些函数都不会取用机锁。
wait()通常被用于“程序正等待着某个条件的成立,但该条件的成立却由线程之外的力量控制着”。你不希望在线程中无所事事地空等。所以wait()使你得以将线程导入休眠状态,直到外在条件改变。只有当notify()或nofityAll()被触发了,该线程才会自休眠状态醒来并检查条件的变化。因此,它提供了线程之间的同步机制。
Java2不赞成使用stop()、suspend()、resume()、destroy(),因为它们都不释放线程所取得的机锁。建议使用旗标来告诉线程离开run()以求终止自己,以此来取代stop()。
所有线程都隶属某个线程群组。这可能是个缺省的群组,或者是你在建立线程时指定的群组。线程诞生的时候会被绑定至某群组,而且从此无法被调换。每个应用程序至少拥有一个隶属“系统线程群组”的线程。如果你在产生线程时未指定群组,那么产生出来的线程都隶属于系统线程群组。可以通过单一命令,在整个线程群组上执行某些动作。
网友评论