NO IMAGE

1.

wait():釋放資源,釋放鎖

sleep():釋放資源,不釋放鎖

wait():Object的方法,用在同步當中,是同步鎖的方法,以鎖控制執行緒

sleep():執行緒類Thread本身的靜態方法

wait(),notify(),notifyAll()方法是用在同步當中的:必須是同步當中的同一把鎖操作執行緒。所以這幾個方法是Object的方法。試想想不在同步中的多執行緒,由於搶奪執行權結果不定,控制無意義,執行緒間也無法通訊:怎樣知道當前是哪個執行緒呢?怎樣捕獲這個執行緒操作它呢?

同步的竅門:如果加了同步還有執行緒安全問題,想兩個前提:1,是不是兩個或兩個以上執行緒 2.用的是不是同一把鎖

只同步了一個執行緒的程式碼是不行的,要將所有執行緒執行的程式碼都用同一把鎖同步,這樣一個執行同步程式碼,其他的判斷同一把鎖,才無法進入自己執行的程式碼

即使在一個Runnable的操作程式碼中是一句, 要同步的也不是一句:是幾個要同步的執行緒要操作的所有程式碼

例子:

class Person
{
String name;
String gender;
boolean flag=false;//標記,預設不輸出
}
class Input implements Runnable
{
private Person p;
private int n=0;
public Input(Person p){
this.p=p;
}
public void run(){
//別忘了while(true)!!
while(true){
synchronized(p){//兩個Runnable用同一個Person初始化,就可保證p是同一個且唯一
if(p.flag){
try{p.wait();}catch(InterruptedException e){}
}
//not "else"
if(n==0){
p.name="Jason";
p.gender="男";
}else{
p.name="Lily";
p.gender="女";
}
n=(n 1)%2;
p.flag=true;//修改標誌位
p.notify();//喚醒另一個
}
}
}
}
class Output implements Runnable
{
private Person p;
public Output(Person p){
this.p=p;
}
public void run(){
//別忘了while(true)!!
while(true){
synchronized(p){
//仍然是判斷標誌位,和上面的執行緒輪流等待,喚醒
if(!p.flag){
try{p.wait();}catch(InterruptedException e){}
}
System.out.println(p.name "..." p.gender);
//別忘了改變標誌位和喚醒另一個
p.flag=false;//讓自己回來等待
p.notify();//同一個鎖,喚醒鎖上的另一個執行緒
}//問題:一個執行緒再次進入鎖,等待,另一個程序在此前喚醒了,此時能進入鎖執行嗎?-->能,wait()即會釋放鎖,也釋放執行資格!
}
}
}
public class InputOutputWaitAndNotify  
{
public static void main(String[] args) 
{
Person p=new Person();
new Thread(new Input(p)).start();
new Thread(new Output(p)).start();
System.out.println("Hello World!");
}
}

結果:

Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男

問題:

一個執行緒再次進入鎖,等待,另一個程序在此前喚醒了,此時能進入鎖執行嗎?

解答:wait()是釋放資源(放棄執行資格)並釋放鎖的,這樣另一個執行緒得以進入並執行

2.一個不用鎖和用鎖的演化探討:未經權威證實和反覆確認的都是錯的,多執行緒往往實驗結果看似正確也是錯的!

不加鎖:用一個if塊中的標誌位判斷和修改:

class Person
{
String name;
String gender;
boolean flag=true;
}
class Input implements Runnable
{
public Input(Person p){
this.p=p;
}
private int n=0;
private Person p;
public void run(){//迴圈,並輪流執行的業務-->由簡模型向完整模型演化,不猶疑,流利,快速思考!!
while(true){
try{Thread.sleep(1000);}catch(InterruptedException e){}
if(p.flag==true){
//try{Thread.sleep(1000);}catch(InterruptedException e){}
if(n%2==0){
p.name="Json";
p.gender="男";
}else{
p.name="Lily";
p.gender="女";
}
n=n 1;
p.flag=false;//這樣下次即使搶到也無法執行內容
}//一個結尾改變狀態的if語句避免多執行緒造成的錯誤
}
}
}
class Output implements Runnable
{
public Output(Person p){
this.p=p;
}
private Person p;
public void run(){
while(true){//這個程式就是無法高效利用執行緒:有一半左右的時間裡搶到執行權的執行緒雖然執行但執行的業務是空的,對業務效率來說很浪費,只有一半左右
try{Thread.sleep(1000);}catch(InterruptedException e){}
if(p.flag==false){//即使上面執行緒執行一半這裡搶到,也因flag未及修改而無法執行內容
//try{Thread.sleep(1000);}catch(InterruptedException e){}
System.out.println(p.name "...." p.gender);
p.flag=true;
}
}
}
}
public class InputOutputOpera0  
{
public static void main(String[] args) 
{
Person p=new Person();
Input in=new Input(p);
Output out=new Output(p);
new Thread(in).start();
new Thread(out).start();
System.out.println("Hello World!");
}
}

增加破壞性實驗:在各種地方設定執行緒sleep

結果:

Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女
Json….男
Lily….女

這個程式的正確性仍有待驗證:實際執行結果和假想執行緒在各處尤其是結束if判斷處被搶走執行權導致判斷失效、錯位的推演仍有可能存在錯誤。最保險的是不斷進行破壞性條件的實驗,和在能同步的業務上都加上同步,保證執行緒安全。可以只將需要同步的同步,將它們分成小塊,只要保證用的是同一把鎖。

我們將上面的程式需要的部分加上同步,結果是不變的,但這種設計仍然讓人覺得不確定不保險,專業的做法仍然是第一個例子:標誌位 輪流等待喚醒機制,這樣可以保證執行緒安全(等待喚醒是鎖上的機制,本來就只能在同步中使用),又可以保證兩個執行緒輪替執行各自的業務程式碼(實際上搶奪執行權仍然是無序的,但我們設定的等待喚醒確保一個執行緒即使連續搶到執行權也可釋放鎖,釋放執行權給另一方(而本方不再執行),保證交替和執行緒業務效率):

class Person
{
String name;
String gender;
boolean flag=true;
}
class Input implements Runnable
{
public Input(Person p){
this.p=p;
}
private int n=0;
private Person p;
public void run(){//迴圈,並輪流執行的業務-->由簡模型向完整模型演化
//如果在這裡同步,另一個執行緒就永遠沒有執行機會了
while(true){
if(p.flag==true){
//把該同步的同步起來
synchronized(p){//同一個鎖
if(n%2==0){
p.name="Json";
p.gender="男";
}else{
p.name="Lily";
p.gender="女";
}
n=n 1;
p.flag=false;
}
//p.flag=false;
}
}
}
}
class Output implements Runnable
{
public Output(Person p){
this.p=p;
}
private Person p;
public void run(){
while(true){
if(p.flag==false){//這樣程式碼中修改了flag下一次即使它搶到執行權也不列印
//如果這裡不用同一個鎖同步,那麼上面的同步也失去作用,因為兩個執行緒還是各行其是,這裡的執行緒不需要鎖,搶奪到
//執行權即可列印,這樣仍然導致錯誤
synchronized(p){
System.out.println(p.name "...." p.gender);
p.flag=true;
}//同步後的問題:兩執行緒搶奪不定,無法交替列印男女:那麼加上標誌位,保證交替執行業務!!
//p.flag=true;
}
}
}
}
public class InputOutputOpera  
{
public static void main(String[] args) 
{
Person p=new Person();
Input in=new Input(p);
Output out=new Output(p);
new Thread(in).start();
new Thread(out).start();
System.out.println("Hello World!");
}
}

為什麼加標誌位:如果只是同步程式碼塊,執行緒執行權搶奪是不定的,無法保證兩個執行緒輪替執行業務,但輪替執行業務部代表執行緒被百分之百利用於做業務,因為在沒有等待喚醒機制的這種程式裡,連續搶奪到執行權的執行緒不是真的不執行,而只是執行內容為空而已,就像第二個例子。