java 多线程 总结_Java 多线程总结
昨天熬了個通宵,看了一晚上的視頻,把java 的多線程相關(guān)技術(shù)重新復(fù)習(xí)了一遍,下面對學(xué)習(xí)過程中遇到的知識點進行下總結(jié)。
首先我們先來了解一下進程、線程、并發(fā)執(zhí)行的概念:
進程是指:一個內(nèi)存中運行的應(yīng)用程序,每個進程都有自己獨立的一塊內(nèi)存空間,一個進程中可以啟動多個線程。比如在Windows系統(tǒng)中,一個運行的exe就是一個進程。
線程是指:進程中的一個執(zhí)行流程,一個進程中可以運行多個線程。比如java.exe進程中可以運行很多線程。線程總是屬于某個進程,進程中的多個線程共享進程的內(nèi)存。
一般來說,當(dāng)運行一個應(yīng)用程序的時候,就啟動了一個進程,當(dāng)然有些會啟動多個進程。啟動進程的時候,操作系統(tǒng)會為進程分配資源,其中最主要的資源是內(nèi)存空間,因為程序是在內(nèi)存中運行的。
在進程中,有些程序流程塊是可以亂序執(zhí)行的,并且這個代碼塊可以同時被多次執(zhí)行。實際上,這樣的代碼塊就是線程體。線程是進程中亂序執(zhí)行的代碼流程。當(dāng)多個線程同時運行的時候,這樣的執(zhí)行模式成為并發(fā)執(zhí)行。
線程的狀態(tài)
1、線程共有下面4種狀態(tài):
新建狀態(tài)(New):新創(chuàng)建了一個線程對象,當(dāng)你用new創(chuàng)建一個線程時,該線程尚未運行。
就緒狀態(tài)(Runnable):線程對象創(chuàng)建后,其他線程調(diào)用了該對象的start()方法。該狀態(tài)的線程位于可運行線程池中,變得可運行,等待獲取CPU的使用權(quán)。
運行狀態(tài)(Running):就緒狀態(tài)的線程獲取了CPU,執(zhí)行程序代碼。
阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因為某種原因放棄CPU使用權(quán),暫時停止運行。直到線程進入就緒狀態(tài),才有機會轉(zhuǎn)到運行狀態(tài)。阻塞的情況分三種:
a. 等待阻塞:運行的線程執(zhí)行wait()方法,JVM會把該線程放入等待池中。
b. 同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM把該線程放入鎖。
c. 其他阻塞:運行的線程執(zhí)行sleep()或join()方法,或者發(fā)出了I/O請求時,JVM會把該線程置為阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉(zhuǎn)入就緒狀態(tài)。
死亡狀態(tài)(Dead):
a. ?由于run方法的正常退出而自然死亡;
b.? 沒有捕獲到的異常事件終止了run方法的執(zhí)行,從而導(dǎo)致線程突然死亡
2、若要確定某個線程當(dāng)前是否活著,可以使用isAlive方法。
如果該線程是可運行線程或者被中斷線程,那么該方法返回true;如果該線程仍然是個新建線程,或者該線程是個死線程,那么該方法返回false
3、注意:你無法確定一個活線程究竟是處于可運行狀態(tài)還是被中斷狀態(tài),也無法確定一個可運行線程是否正處在運行之中。另外,你也無法對尚未成為可運行的線程與已經(jīng)死掉的線程進行區(qū)分。
4、線程必須退出中斷狀態(tài),并且返回到可運行狀態(tài),方法是使用與進入中斷狀態(tài)相反的過程:
a.如果線程已經(jīng)處于睡眠狀態(tài),就必須經(jīng)過規(guī)定的毫秒數(shù)
b.如果線程正在等待輸入或輸出操作完成,那么必須等待該操作完成
c.如果線程調(diào)用了wait方法,那么另外一個線程必須調(diào)用notifyAll或者notify方法
d.如果線程正在等待另一個線程擁有的對象鎖,那么另一個線程必須放棄該鎖的所有權(quán)
5、下面這副圖很好的反映了線程在不同情況下的狀態(tài)變化。
了解完多線程的相關(guān)知識,下面來介紹一下在java中多線程的實現(xiàn)方式
JAVA多線程實現(xiàn)方式
JAVA多線程實現(xiàn)方式主要有以下三種:
1、繼承Thread類
2、實現(xiàn)Runnable接口
3、使用ExecutorService、Callable、Future實現(xiàn)有返回結(jié)果的多線程。
其中前兩種方式線程執(zhí)行完后都沒有返回值,只有最后一種是帶返回值的。其中最常用的也是前兩種實現(xiàn)方式。下面對前兩種實現(xiàn)方式分別做下講解。
1、繼承Thread類實現(xiàn)多線程
繼承Thread類的方法盡管被我列為一種多線程實現(xiàn)方式,但Thread本質(zhì)上也是實現(xiàn)了Runnable接口的一個實例,它代表一個線程的實例,并且,啟動線程的唯一方法就是通過Thread類的start()實例方法。start()方法是一個native方法,它將啟動一個新線程,并執(zhí)行run()方法。這種方式實現(xiàn)多線程很簡單,通過自己的類直接extend Thread,并復(fù)寫run()方法,就可以啟動新線程并執(zhí)行自己定義的run()方法。
例如:
package thread;
public class MyThread extends Thread {
public void run() {
System.out.println("run()方法正在執(zhí)行");
}
}
啟動線程方式如下:
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
2、實現(xiàn)Runnable接口方式實現(xiàn)多線程
如果自己的類已經(jīng)extends另一個類,就無法直接extends Thread,此時,必須實現(xiàn)一個Runnable接口。
方法如下:
package thread;
class OtherClass{
public void print(String str){
System.out.println(str);
}
}
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("run()正在執(zhí)行");
}
}
為了啟動MyThread,需要首先實例化一個Thread,并傳入自己的MyThread實例。
具體方法如下:
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
事實上,當(dāng)傳入一個Runnable target參數(shù)給Thread后,Thread的run()方法就會調(diào)用target.run(),參考JDK源代碼:
public void run() {
if (target != null) {
target.run();
}
}
學(xué)會了線程的創(chuàng)建方式,下面我們在舉幾個線程狀態(tài)轉(zhuǎn)換的例子
3、線程狀態(tài)的轉(zhuǎn)換實例
package thread;
public class ThreadStateDemo extends Thread {
Thread thread;
public ThreadStateDemo() {
thread = new Thread(this);
System.out.println("創(chuàng)建一個線程:thread");
thread.start();
}
public void run() {
try {
System.out.println("線程thread正在運行!");
System.out.println("線程thread睡眠3秒中...!");
Thread.sleep(3000); //靜態(tài)方法,使當(dāng)前正在執(zhí)行的線程睡眠3秒
System.out.println("線程thread在睡眠后重新運行!");
}catch(InterruptedException e) {
System.out.println("線程被中斷");
}
}
public static void main(String[] args) {
new ThreadStateDemo();
System.out.println("主線程main結(jié)束!");
}
}
【運行結(jié)果】如下:
創(chuàng)建一個線程:thread
主線程main結(jié)束!
線程thread正在運行!
線程thread睡眠3秒中...!
線程thread在睡眠后重新運行!
終止線程的實例:
package thread;
public class ThreadShutDownDemo {
public static void main(String args[]) {
Runner runner = new Runner();
Thread thread = new Thread(runner);
thread.start();
for(int i=0;i<10;i++) {
if(i%10!=0) {
System.out.println("在主線程中 i=" + i);
}
}
System.out.println("主線程main結(jié)束");
//通知線程結(jié)束
runner.shutDown();
}
}
class Runner implements Runnable {
//控制線程是否結(jié)束
private boolean flag = true;
public void run() {
int i=0;
while(flag == true) {
System.out.println("在子線程中 i=" + i++);
}
System.out.println("子線程結(jié)束");
}
//設(shè)置線程結(jié)束標志
public void shutDown() {
flag = false;
}
}
【運行結(jié)果】如下:
在主線程中 i=1
在子線程中 i=0
在主線程中 i=2
在子線程中 i=1
在主線程中 i=3
在子線程中 i=2
在主線程中 i=4
在子線程中 i=3
在主線程中 i=5
在子線程中 i=4
在主線程中 i=6
在主線程中 i=7
在主線程中 i=8
在主線程中 i=9
主線程main結(jié)束
在子線程中 i=5
子線程結(jié)束
join()方法實例:
package thread;
public class TheadJoinDemo {
public static void main(String[] args) {
Runner2 r = new Runner2();
Thread t = new Thread(r);
t.start();
try {
t.join();//主線程main將中斷,直到線程t執(zhí)行完畢
}catch(InterruptedException e) {
}
for(int i=0;i<5;i++) {
System.out.println("主線程:" + i);
}
}
}
class Runner2 implements Runnable {
public void run() {
for(int i=0;i<10;i++) {
System.out.println("子線程:" + i);
}
}
}
【運行結(jié)果】如下:
子線程:0
子線程:1
子線程:2
子線程:3
子線程:4
子線程:5
子線程:6
子線程:7
子線程:8
子線程:9
主線程:0
主線程:1
主線程:2
主線程:3
主線程:4
介紹完以上幾個實例,我們下面對sleep()、wait()、yeid()、join()幾個方法進行下區(qū)別總結(jié)
sleep方法與wait方法的區(qū)別:
sleep方法是靜態(tài)方法,wait方法是非靜態(tài)方法。
sleep方法在時間到后會自己“醒來”,但wait不能,必須由其它線程通過notify(All)方法讓它“醒來”。
sleep方法通常用在不需要等待資源情況下的阻塞,像等待線程、數(shù)據(jù)庫連接的情況一般用wait。
sleep/wait與yeld方法的區(qū)別:
調(diào)用sleep或wait方法后,線程即進入block狀態(tài),而調(diào)用yeld方法后,線程進入runnable狀態(tài)。
wait與join方法的區(qū)別:
wait方法體現(xiàn)了線程之間的互斥關(guān)系,而join方法體現(xiàn)了線程之間的同步關(guān)系。
wait方法必須由其它線程來解鎖,而join方法不需要,只要被等待線程執(zhí)行完畢,當(dāng)前線程自動變?yōu)榫途w。
join方法的一個用途就是讓子線程在完成業(yè)務(wù)邏輯執(zhí)行之前,主線程一直等待直到所有子線程執(zhí)行完畢。
線程的同步問題
在實際應(yīng)用中,我們通常會遇到多線程安全問題。多線程安全問題:當(dāng)多條語句在操作同一線程共享數(shù)據(jù)是,一個線程對多條語句只執(zhí)行了一部分,還沒有執(zhí)行完, 此時另一個線程參與進來執(zhí)行,導(dǎo)致共享數(shù)據(jù)的錯誤。
解決辦法:
對多條操作共享數(shù)據(jù)的語句,只能讓一個線程都執(zhí)行完,在執(zhí)行過程中,其他線程不可以參與執(zhí)行。
Java 對于多線程的安全提供了專業(yè)的解決方式。
線程的同步是保證多線程安全訪問競爭資源的一種手段,對于同步,在具體的Java代碼中需要完成一下兩個操作:
把競爭訪問的資源標識為private;
同步哪些修改變量的代碼,使用synchronized關(guān)鍵字同步方法或代碼。
synchronized(對象){
代碼塊
...
}
同步的前提:
1、必須要有兩個或者兩個以上的線程運行;
2、必須是多個線程使用同一個鎖;
好處:解決了多線程的安全問題;
弊端:多個線程需要判斷鎖,較為消耗資源;
注意:非靜態(tài)同步函數(shù)的對象鎖為this,靜態(tài)同步函數(shù)所使用的鎖是該方法所在類的字節(jié)碼文件對象,即類名.class,靜態(tài)方法里的同步鎖都是使用的是類的字節(jié)碼對象。
//靜態(tài)同步函數(shù)鎖
public static synchronized void show(){
ticket++;
System.out.println(Thread.currentThread().getName()+"runtime..."+ticket--);
}
下面來例舉一個線程同步的例子:(同步方法)
package thread;
public class SynchronizedThread {
public static void main(String[] args) {
User u = new User("王某", 100);
MyThread2 t1 = new MyThread2("線程A", u, 10);
MyThread2 t2 = new MyThread2("線程B", u, -50);
MyThread2 t3 = new MyThread2("線程C", u, -60);
MyThread2 t4 = new MyThread2("線程D", u, -40);
MyThread2 t5 = new MyThread2("線程E", u, 20);
MyThread2 t6 = new MyThread2("線程F", u, 28);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class MyThread2 extends Thread {
private User u;
private int y = 0;
MyThread2(String name, User u, int y) {
super(name);
this.u = u;
this.y = y;
}
public void run() {
u.oper(y);
}
}
class User {
private String code;
private int cash;
User(String code, int cash) {
this.code = code;
this.cash = cash;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
/**
* 業(yè)務(wù)方法
* @param x 添加x萬元
*/
public synchronized void oper(int x) {
try {
Thread.sleep(10L);
this.cash += x;
System.out.println(Thread.currentThread().getName() + "運行結(jié)束,增加“" + x + "”,當(dāng)前用戶賬戶余額為:" + cash);
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "User{" +
"code='" + code + '\'' +
", cash=" + cash +
'}';
}
}
【運行結(jié)果】如下:
線程A運行結(jié)束,增加“10”,當(dāng)前用戶賬戶余額為:110
線程F運行結(jié)束,增加“28”,當(dāng)前用戶賬戶余額為:138
線程E運行結(jié)束,增加“20”,當(dāng)前用戶賬戶余額為:158
線程D運行結(jié)束,增加“-40”,當(dāng)前用戶賬戶余額為:118
線程C運行結(jié)束,增加“-60”,當(dāng)前用戶賬戶余額為:58
線程B運行結(jié)束,增加“-50”,當(dāng)前用戶賬戶余額為:8
下面是線程不同步的情況,也就是去掉oper(int x)方法的synchronized修飾符,然后再運行程序
【運行結(jié)果】如下:
線程F運行結(jié)束,增加“28”,當(dāng)前用戶賬戶余額為:128
線程D運行結(jié)束,增加“-40”,當(dāng)前用戶賬戶余額為:88
線程B運行結(jié)束,增加“-50”,當(dāng)前用戶賬戶余額為:38
線程E運行結(jié)束,增加“20”,當(dāng)前用戶賬戶余額為:58
線程C運行結(jié)束,增加“-60”,當(dāng)前用戶賬戶余額為:-2
線程A運行結(jié)束,增加“10”,當(dāng)前用戶賬戶余額為:8
很顯然,上面的結(jié)果是錯誤的,導(dǎo)致錯誤的原因是多個線程并發(fā)訪問了競爭資源u,并對u的屬性做了改動。
注意:當(dāng)去掉synchronized修飾符后,線程不在同步,每次運行的結(jié)果將都不一樣,可見同步的重要性。
再把以上實例改為同步代碼塊方式
對于同步,除了同步方法外,還可以使用同步代碼塊,有時候同步代碼塊會帶來比同步方法更好的效果。
追其同步的根本的目的,是控制競爭資源的正確的訪問,因此只要在訪問競爭資源的時候保證同一時刻只能一個線程訪問即可,因此Java引入了同步代碼快的策略,以提高性能。
在上個例子的基礎(chǔ)上,對oper方法做了改動,由同步方法改為同步代碼塊模式。代碼如下:
package thread;
/**
* 同步代碼塊
* @author Chu
*
*/
public class SynchronizedThread2 {
public static void main(String[] args) {
User u = new User("張三", 100);
MyThread3 t1 = new MyThread3("線程A", u, 10);
MyThread3 t2 = new MyThread3("線程B", u, -50);
MyThread3 t3 = new MyThread3("線程C", u, -60);
MyThread3 t4 = new MyThread3("線程D", u, -40);
MyThread3 t5 = new MyThread3("線程E", u, 20);
MyThread3 t6 = new MyThread3("線程F", u, 28);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
class MyThread3 extends Thread {
private User u;
private int y = 0;
MyThread3(String name, User u, int y) {
super(name);
this.u = u;
this.y = y;
}
public void run() {
u.oper(y);
}
}
class User2 {
private String code;
private int cash;
User2(String code, int cash) {
this.code = code;
this.cash = cash;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
/**
* 業(yè)務(wù)方法
* @param x 添加x萬元
*/
public void oper(int x) {
try {
Thread.sleep(10L);
synchronized (this) {
this.cash += x;
System.out.println(Thread.currentThread().getName() + "運行結(jié)束,增加“" + x + "”,當(dāng)前用戶賬戶余額為:" + cash);
}
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "User{" +
"code='" + code + '\'' +
", cash=" + cash +
'}';
}
}
【運行結(jié)果】如下:
線程A運行結(jié)束,增加“10”,當(dāng)前用戶賬戶余額為:110
線程F運行結(jié)束,增加“28”,當(dāng)前用戶賬戶余額為:138
線程D運行結(jié)束,增加“-40”,當(dāng)前用戶賬戶余額為:98
線程E運行結(jié)束,增加“20”,當(dāng)前用戶賬戶余額為:118
線程C運行結(jié)束,增加“-60”,當(dāng)前用戶賬戶余額為:58
線程B運行結(jié)束,增加“-50”,當(dāng)前用戶賬戶余額為:8
用到線程的同步,隨之可能會帶來死鎖問題。
導(dǎo)致死鎖的原因:兩個線程互相等待競爭資源,導(dǎo)致兩邊都無法得到資源,而使自己無法運行。
下面例舉一個導(dǎo)致死鎖的一個實例,代碼如下:
package thread;
class Demo1{
static Object obj1=new Object();
static Object obj2=new Object();
}
class Demo2 implements Runnable{
boolean flag;
Demo2(boolean flag){
this.flag=flag;
}
@Override
public void run(){
if(flag){
while(true){
synchronized(Demo1.obj1){
System.out.println("1");
synchronized(Demo1.obj2){
System.out.println("2");
}
}
}
}
else{
while(true){
synchronized(Demo1.obj2){
System.out.println("2");
synchronized(Demo1.obj1){
System.out.println("1");
}
}
}
}
}
}
最后我再說說:生產(chǎn)者消費者的問題
對于多線程程序來說,不管任何編程語言,生產(chǎn)者和消費者模型都是最經(jīng)典的。
實際上,準確說應(yīng)該是“生產(chǎn)者-消費者-倉儲”模型,離開了倉儲,生產(chǎn)者消費者模型就顯得沒有說服力了。
對于此模型,應(yīng)該明確一下幾點:
1、生產(chǎn)者僅僅在倉儲未滿時候生產(chǎn),倉滿則停止生產(chǎn);
2、消費者僅僅在倉儲有產(chǎn)品時候才能消費,倉空則等待;
3、當(dāng)消費者發(fā)現(xiàn)倉儲沒產(chǎn)品可消費時候會通知生產(chǎn)者生產(chǎn);
4、生產(chǎn)者在生產(chǎn)出可消費產(chǎn)品時候,應(yīng)該通知等待的消費者去消費。
此模型將要結(jié)合java.lang.Object的wait與notify、notifyAll方法來實現(xiàn)以上的需求。這是非常重要的。
具體實現(xiàn)代碼如下:
package thread;
/**
* Java線程:生產(chǎn)者消費者模型
* @author Chu 2013-06-15 05:32:29
*/
public class ProductTest {
public static void main(String[] args) {
Godown godown = new Godown(20);
Consumer c1 = new Consumer(80, godown);
Consumer c2 = new Consumer(30, godown);
Consumer c3 = new Consumer(20, godown);
Producer p1 = new Producer(5, godown);
Producer p2 = new Producer(5, godown);
Producer p3 = new Producer(5, godown);
Producer p4 = new Producer(10, godown);
Producer p5 = new Producer(20, godown);
Producer p6 = new Producer(35, godown);
Producer p7 = new Producer(50, godown);
c1.start();
c2.start();
c3.start();
p1.start();
p2.start();
p3.start();
p4.start();
p5.start();
p6.start();
p7.start();
}
}
/** 倉庫 */
class Godown {
public static final int max_size = 100; //最大庫存量
public int curnum; //當(dāng)前庫存量
Godown() {
}
Godown(int curnum) {
this.curnum = curnum;
}
/**
* 生產(chǎn)指定數(shù)量的產(chǎn)品
* @param neednum
*/
public synchronized void produce(int neednum) {
//測試是否需要生產(chǎn)
while (neednum + curnum > max_size) {
System.out.println("要生產(chǎn)的產(chǎn)品數(shù)量" + neednum + "超過剩余庫存量" + (max_size - curnum) + ",暫時不能執(zhí)行生產(chǎn)任務(wù)!");
try {
//當(dāng)前的生產(chǎn)線程等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//滿足生產(chǎn)條件,則進行生產(chǎn),這里簡單的更改當(dāng)前庫存量
curnum += neednum;
System.out.println("已經(jīng)生產(chǎn)了" + neednum + "個產(chǎn)品,現(xiàn)倉儲量為" + curnum);
//喚醒在此對象監(jiān)視器上等待的所有線程
notifyAll();
}
/**
* 消費指定數(shù)量的產(chǎn)品
* @param neednum
*/
public synchronized void consume(int neednum) {
//測試是否可消費
while (curnum < neednum) {
try {
//當(dāng)前的生產(chǎn)線程等待
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//滿足消費條件,則進行消費,這里簡單的更改當(dāng)前庫存量
curnum -= neednum;
System.out.println("已經(jīng)消費了" + neednum + "個產(chǎn)品,現(xiàn)倉儲量為" + curnum);
//喚醒在此對象監(jiān)視器上等待的所有線程
notifyAll();
}
}
/** 生產(chǎn)者 */
class Producer extends Thread {
//生產(chǎn)產(chǎn)品的數(shù)量
private int neednum;
//倉庫
private Godown godown;
Producer(int neednum, Godown godown) {
this.neednum = neednum;
this.godown = godown;
}
public void run() {
//生產(chǎn)指定數(shù)量的產(chǎn)品
godown.produce(neednum);
}
}
/** 消費者 */
class Consumer extends Thread {
//生產(chǎn)產(chǎn)品的數(shù)量
private int neednum;
//倉庫
private Godown godown;
Consumer(int neednum, Godown godown) {
this.neednum = neednum;
this.godown = godown;
}
public void run() {
//消費指定數(shù)量的產(chǎn)品
godown.consume(neednum);
}
}
【運行結(jié)果】如下:
已經(jīng)消費了20個產(chǎn)品,現(xiàn)倉儲量為0
已經(jīng)生產(chǎn)了5個產(chǎn)品,現(xiàn)倉儲量為5
已經(jīng)生產(chǎn)了5個產(chǎn)品,現(xiàn)倉儲量為10
已經(jīng)生產(chǎn)了5個產(chǎn)品,現(xiàn)倉儲量為15
已經(jīng)生產(chǎn)了20個產(chǎn)品,現(xiàn)倉儲量為35
已經(jīng)生產(chǎn)了50個產(chǎn)品,現(xiàn)倉儲量為85
已經(jīng)消費了80個產(chǎn)品,現(xiàn)倉儲量為5
已經(jīng)生產(chǎn)了10個產(chǎn)品,現(xiàn)倉儲量為15
已經(jīng)生產(chǎn)了35個產(chǎn)品,現(xiàn)倉儲量為50
已經(jīng)消費了30個產(chǎn)品,現(xiàn)倉儲量為20
說明:
對于本例,要說明的是當(dāng)發(fā)現(xiàn)不能滿足生產(chǎn)或者消費條件的時候,調(diào)用對象的wait方法,wait方法的作用是釋放當(dāng)前線程的所獲得的鎖,并調(diào)用對象的notifyAll() 方法,通知(喚醒)該對象上其他等待線程,使得其繼續(xù)執(zhí)行。這樣,整個生產(chǎn)者、消費者線程得以正確的協(xié)作執(zhí)行。
notifyAll() 方法,起到的是一個通知作用,不釋放鎖,也不獲取鎖。只是告訴該對象上等待的線程可以競爭執(zhí)行了。
以上這個例子僅僅是生產(chǎn)者消費者模型中最簡單的一種表示,在這個例子中,如果消費者消費的倉儲量達不到滿足,而又沒有生產(chǎn)者,則程序會一直處于等待狀態(tài),這當(dāng)然是不對的。實際上可以將此例進行修改,修改為,根據(jù)消費驅(qū)動生產(chǎn),同時生產(chǎn)兼顧倉庫,如果倉不滿就生產(chǎn),并對每次最大消費量做個限制,這樣就不存在此問題了,當(dāng)然這樣的例子更復(fù)雜,更難以說明這樣一個簡單模型。
總結(jié)
以上是生活随笔為你收集整理的java 多线程 总结_Java 多线程总结的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不想备案证可以进户吗(不想备案)
- 下一篇: java integer valueof