国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

Java多線程(二)

2019-11-06 06:59:22
字體:
供稿:網(wǎng)友

一、使用同步解決買票問題

解決資源共享的同步操作問題,可以使用同步代碼塊和同步方法兩種方式。

第一種:使用同步代碼塊必須指定一個(gè)需要同步的對(duì)象,通常將當(dāng)前對(duì)象(this)設(shè)置成同步對(duì)象。

class SaleTask implements Runnable{	PRivate int ticket = 5;//剩余票數(shù)	public void run() {		if(ticket>0){			System.out.println("線程"+Thread.currentThread().getName()+"需要賣票");			synchronized (this) {				if(ticket>0){					try {						Thread.sleep(2000);					} catch (InterruptedException e) {						e.printStackTrace();					}					System.out.println("線程"+Thread.currentThread().getName()+							"賣了一張票,剩余票數(shù)"+(--ticket)+"張");				}else{					System.out.println("線程"+Thread.currentThread().getName()+"需要賣票,已無票");				}			}		}else{			System.out.println("線程"+Thread.currentThread().getName()+"需要賣票,已無票");		}	}}public class Main {	public static void main(String[] args){		SaleTask sale = new SaleTask();		for(int i=0;i<8;i++){	//有8個(gè)線程需要賣票			Thread t1 = new Thread(sale,"thread"+(i+1));			t1.start();		}	}}

運(yùn)行結(jié)果:

線程thread1需要賣票線程thread3需要賣票線程thread2需要賣票線程thread6需要賣票線程thread7需要賣票線程thread4需要賣票線程thread8需要賣票線程thread5需要賣票線程thread1賣了一張票,剩余票數(shù)4張線程thread5賣了一張票,剩余票數(shù)3張線程thread8賣了一張票,剩余票數(shù)2張線程thread4賣了一張票,剩余票數(shù)1張線程thread7賣了一張票,剩余票數(shù)0張線程thread6需要賣票,已無票線程thread2需要賣票,已無票線程thread3需要賣票,已無票第二種:同步方法

	public synchronized void run() {		if(ticket>0){			System.out.println("線程"+Thread.currentThread().getName()+					"需要賣票");			if(ticket>0){				try {					Thread.sleep(2000);				} catch (InterruptedException e) {					e.printStackTrace();				}				System.out.println("線程"+Thread.currentThread().getName()+						"賣了一張票,剩余票數(shù)"+(--ticket)+"張");			}else{				System.out.println("線程"+Thread.currentThread().getName()+						"需要賣票,已無票");			}		}else{			System.out.println("線程"+Thread.currentThread().getName()+					"需要賣票,已無票");		}	}

運(yùn)行結(jié)果:

線程thread1需要賣票線程thread1賣了一張票,剩余票數(shù)4張線程thread7需要賣票線程thread7賣了一張票,剩余票數(shù)3張線程thread3需要賣票線程thread3賣了一張票,剩余票數(shù)2張線程thread8需要賣票線程thread8賣了一張票,剩余票數(shù)1張線程thread4需要賣票線程thread4賣了一張票,剩余票數(shù)0張線程thread6需要賣票,已無票線程thread5需要賣票,已無票線程thread2需要賣票,已無票

二、死鎖

多個(gè)線程共享同一資源時(shí)需要進(jìn)行同步,以保證資源操作的完整性,但是過多的同步就有可能產(chǎn)生死鎖。

class ThreadTask implements Runnable{	private static String resourceA = "Resource A";	private static String resourceB = "Resource B";	public boolean flag;//標(biāo)記獲取資源的順序,true表示先獲取A,再獲取B;false則相反		public void run() {		if(flag){			synchronized(resourceA){				System.out.println("線程"+Thread.currentThread().getName()+"獲取了資源A");				try{					Thread.sleep(500);				}catch(InterruptedException e){					e.printStackTrace();				}				synchronized(resourceB){					System.out.println("線程"+Thread.currentThread().getName()+"獲取了資源B");				}			}		}		else{			synchronized(resourceB){				System.out.println("線程"+Thread.currentThread().getName()+"獲取了資源B");				try{					Thread.sleep(500);				}catch(InterruptedException e){					e.printStackTrace();				}				synchronized(resourceA){					System.out.println("線程"+Thread.currentThread().getName()+"獲取了資源A");				}			}		}	}}public class Main {	public static void main(String[] args){		ThreadTask task1 = new ThreadTask();		ThreadTask task2 = new ThreadTask();		task1.flag = true;		task2.flag = false;		Thread t1 = new Thread(task1,"thread-1");		Thread t2 = new Thread(task2,"thread-2");		t1.start();		t2.start();	}}

運(yùn)行結(jié)果:

線程thread-1獲取了資源A線程thread-2獲取了資源B 兩個(gè)線程都在等待對(duì)方釋放手中的資源,這樣就產(chǎn)生了死鎖。

三、線程操作案例——生產(chǎn)者與消費(fèi)者

線程操作中一個(gè)典型的案例——生產(chǎn)者與消費(fèi)者問題,生產(chǎn)者不斷生產(chǎn),消費(fèi)者不斷消費(fèi)生產(chǎn)者生產(chǎn)的產(chǎn)品。

從中可以看出,生產(chǎn)者產(chǎn)生資源后將資源放到一個(gè)區(qū)域中,消費(fèi)者從這個(gè)區(qū)域中取出產(chǎn)品,由于線程運(yùn)行的不確定性,可能會(huì)產(chǎn)生下面兩種問題:

1)生產(chǎn)者剛向數(shù)據(jù)存儲(chǔ)空間中添加了信息的名稱,還沒有添加信息的內(nèi)容,程序就切換到了消費(fèi)者線程,這樣消費(fèi)者線程讀取到的是更新后的信息的名稱和上一次信息的內(nèi)容。

2)生產(chǎn)者連續(xù)更新了多次信息,消費(fèi)者才開始讀取,或者,消費(fèi)者連續(xù)多次讀取信息,生產(chǎn)者卻還沒來及更新。

1.程序的基本實(shí)現(xiàn)

無論是生產(chǎn)者還是消費(fèi)者,操作的都是信息(資源),所以需要定義一個(gè)信息類。

//信息類(資源)class Info {	private String name = "李興華";						//資源name	private String content = "java講師";					//資源content		public void setName(String name){		this.name = name;	}	public String getName(){		return name;	}	public void setContent(String content){		this.content = content;	}	public String getContent(){		return content;	}}

生產(chǎn)者和消費(fèi)者相當(dāng)于兩個(gè)線程,操作同一個(gè)空間,分別實(shí)現(xiàn)Runnable接口

//生產(chǎn)者線程class Producer implements Runnable{	private Info info = null;						//資源的引用		public Producer(Info info){		this.info = info;	}	public void run() {		boolean flag = false;		for(int i=0;i<50;i++){						//循環(huán)50次讓生產(chǎn)者生產(chǎn)具體的內(nèi)容			if(flag){				this.info.setName("李興華");				try{					Thread.sleep(90);				}catch(InterruptedException e){					e.printStackTrace();				}				this.info.setContent("Java講師");				flag = false;			}else{				this.info.setName("mldn");				try{					Thread.sleep(90);				}catch(InterruptedException e){					e.printStackTrace();				}				this.info.setContent("www.mldnjava.cn");				flag = true;			}		}	}}
//消費(fèi)者線程class Consumer implements Runnable{	private Info info = null;						//資源的引用		public Consumer(Info info){		this.info = info;	}	public void run() {		for(int i=0;i<50;i++){			try{				Thread.sleep(110);			}catch(InterruptedException e){				e.printStackTrace();			}			System.out.println(this.info.getName()+"--->"+this.info.getContent());		}	}}

主方法:

public class Main {	public static void main(String[] args){		Info i = new Info();		Producer p = new Producer(i);		Consumer c = new Consumer(i);		Thread pt = new Thread(p,"thread-1");		Thread ct = new Thread(c,"thread-2");		pt.start();		ct.start();	}}

運(yùn)行結(jié)果:

李興華--->www.mldnjava.cnmldn--->Java講師李興華--->www.mldnjava.cnmldn--->Java講師mldn--->Java講師李興華--->www.mldnjava.cnmldn--->Java講師李興華--->www.mldnjava.cn李興華--->www.mldnjava.cnmldn--->Java講師......

2.解決問題1——加入同步

將設(shè)置信息的方法和獲取信息的方法都設(shè)為同步方法,使得無論是設(shè)置信息還是讀取信息,都需要先獲取信息對(duì)象,在一段時(shí)間內(nèi),只有一個(gè)線程可以對(duì)資源進(jìn)行操作。

//信息類(資源)class Info {	private String name = "李興華";						//資源name	private String content = "Java講師";					//資源content	public synchronized void set(String name,String content){		setName(name);		try{			Thread.sleep(90);					//加入延遲		}catch(InterruptedException e){			e.printStackTrace();		}		setContent(content);	}	public synchronized void get(){		try{			Thread.sleep(100);					//加入延遲		}catch(InterruptedException e){			e.printStackTrace();		}		System.out.println(getName()+"--->"+getContent());	}	public void setName(String name){		this.name = name;	}	public String getName(){		return name;	}	public void setContent(String content){		this.content = content;	}	public String getContent(){		return content;	}}

生產(chǎn)者:

//生產(chǎn)者線程class Producer implements Runnable{	private Info info = null;						//資源的引用		public Producer(Info info){		this.info = info;	}	public void run() {		boolean flag = false;		for(int i=0;i<50;i++){			if(flag){				this.info.set("李興華", "Java講師");				flag = false;			}else{				this.info.set("mldn", "www.mldnjava.cn");				flag = true;			}		}	}}

消費(fèi)者:

//消費(fèi)者線程class Consumer implements Runnable{	private Info info = null;						//資源的引用		public Consumer(Info info){		this.info = info;	}	public void run() {		for(int i=0;i<50;i++){			this.info.get();		}	}}

mldn--->www.mldnjava.cn李興華--->Java講師mldn--->www.mldnjava.cnmldn--->www.mldnjava.cn李興華--->Java講師李興華--->Java講師李興華--->Java講師mldn--->www.mldnjava.cn李興華--->Java講師李興華--->Java講師......

從運(yùn)行結(jié)果可以看出,信息錯(cuò)亂的問題解決了,但是仍然存在信息重復(fù)讀取的問題,既然有重復(fù)讀取,就有重復(fù)設(shè)置,對(duì)于這樣的問題需要用到Object類。

 

3.Object類對(duì)線程的支持

1.Object類有以下幾種方法是對(duì)線程支持的:

 

從表中可知,可以將線程設(shè)為等待狀態(tài),也可以喚醒線程。喚醒線程的方法有兩個(gè),所有等待的線程一般會(huì)按照順序排列,如果用notify()方法喚醒,則會(huì)喚醒第一個(gè)線程,若用notifyAll()方法喚醒,則會(huì)喚醒所有的等待線程,哪一個(gè)線程的優(yōu)先級(jí)高,哪一個(gè)線程就有可能先執(zhí)行。

 

2.解決問題2——加入等待與喚醒

如果想讓生產(chǎn)者不重復(fù)生產(chǎn),消費(fèi)者不重復(fù)讀取,可以增加一個(gè)標(biāo)志位,標(biāo)志位為boolean型,

若為true,表示生產(chǎn)者可以生產(chǎn),但消費(fèi)者不可以讀取,如果是消費(fèi)者線程則需要等待;

若為false,表示消費(fèi)者可以讀取,但生產(chǎn)者不可以生產(chǎn),如果是生產(chǎn)者線程則需要等待。

 

要完成以上功能,直接修改Info類即可,在Info類中增加一個(gè)標(biāo)志位,通過判斷標(biāo)志位完成等待與喚醒操作。

//信息類(資源)class Info {         privateString name = "李興華";                                                //資源name         privateString content = "Java講師";                                           //資源content         privateboolean flag = false;                                                  //true,表示可以生產(chǎn),不能消費(fèi)                                                                                       //false表示可以消費(fèi),不能生產(chǎn)         //生產(chǎn)資源         publicsynchronized void set(String name,String content){                   if(!flag){                                                          //為false時(shí),生產(chǎn)者線程等待                            try{                                     super.wait();                                     //wait()方法會(huì)讓線程釋放手中的鎖                            }catch (InterruptedException e) {                                     e.printStackTrace();                            }                   }                   setName(name);                   try{                            Thread.sleep(90);                                          //加入延遲                   }catch(InterruptedExceptione){                            e.printStackTrace();                   }                   setContent(content);                   this.flag= false;                   super.notify();                                                     //喚醒等待的線程         }         //消費(fèi)資源         publicsynchronized void get(){                   if(flag){                            try{                                     super.wait();                            }catch (InterruptedException e) {                                     e.printStackTrace();                            }                   }                   try{                            Thread.sleep(100);                                          //加入延遲                   }catch(InterruptedExceptione){                            e.printStackTrace();                   }                   System.out.println(getName()+"--->"+getContent());                   this.flag= true;                   super.notify();                                                      //喚醒等待的線程         }                 publicvoid setName(String name){                   this.name= name;         }         publicString getName(){                   returnname;         }         publicvoid setContent(String content){                   this.content= content;         }         publicString getContent(){                   returncontent;         }}

運(yùn)行結(jié)果:

李興華--->Java講師mldn--->www.mldnjava.cn李興華--->Java講師mldn--->www.mldnjava.cn李興華--->Java講師mldn--->www.mldnjava.cn李興華--->Java講師mldn--->www.mldnjava.cn李興華--->Java講師mldn--->www.mldnjava.cn......

從運(yùn)行結(jié)果可以發(fā)現(xiàn),生產(chǎn)者每生產(chǎn)一個(gè)資源就要等待消費(fèi)者取走,消費(fèi)者每取走一個(gè)就要等待生產(chǎn)者生產(chǎn),這樣就避免了重復(fù)生產(chǎn)和重復(fù)取走的問題。

 

四、線程的生命周期

 

其中3個(gè)方法:

suspend():暫時(shí)掛起線程

resume():回復(fù)掛起的線程

stop():停止線程

但這三種方法不推薦使用,因?yàn)槭褂眠@三種方法會(huì)產(chǎn)生死鎖的問題,在源代碼中這三個(gè)方法使用了@Deprecated


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 密云县| 新宾| 蒙自县| 府谷县| 汉川市| 临汾市| 句容市| 肥城市| 杨浦区| 海宁市| 广东省| 宁海县| 井陉县| 施秉县| 托克逊县| 乌兰察布市| 墨脱县| 金昌市| 澄城县| 鞍山市| 龙井市| 广德县| 汾西县| 高陵县| 枝江市| 温州市| 松潘县| 额尔古纳市| 南昌县| 彭山县| 香港 | SHOW| 米林县| 汝阳县| 绩溪县| 安陆市| 灵宝市| 进贤县| 墨脱县| 新民市| 岑溪市|