java获取其他类的线程,使用Java实现面向对象编程——第七章 多线程
1、進程:是指運行中的應(yīng)用程序,每個進程都有自己獨立的地址空間(內(nèi)存空間);
Eg:用戶點擊桌面的IE瀏覽器,就啟動了一個進程,操作系統(tǒng)就會為該進程分配獨立的地址空間。當(dāng)用戶再次點擊左面的IE瀏覽器,又啟動了一個進程,操作系統(tǒng)將為新的進程分配新的獨立的地址空間。目前操作系統(tǒng)都支持多進程。
◆注;用戶每啟動一個進程,操作系統(tǒng)就會為該進程分配一個獨立的內(nèi)存空間。
◆進程的特點:進程是系統(tǒng)運行程序的基本單位;
每一個程序都有自己獨立的一塊內(nèi)存空間、一組系統(tǒng)資源;
每一個進程的內(nèi)部數(shù)據(jù)和狀態(tài)都是完全獨立的;
2、線程:是進程中執(zhí)行元算的最小單位;可以完成一個獨立的順序控制流程;每個進程中,必須至少建立一個線程(主線程)來作為程序運行的入口點;
◆附加:
線程:是進程中的一個實體,是被系統(tǒng)獨立調(diào)度和分派的基本單位,線程自己不擁有系統(tǒng)資源,只擁有一點在運行中必不可少的資源,
但它可與同屬一個進程的其它線程共享進程所擁有的全部資源。一個線程可以創(chuàng)建和撤消另一個線程,
同一進程中的多個線程之間可以并發(fā)執(zhí)行。
線程:
1、線程是輕量級的進程
2、線程沒有獨立的地址空間(內(nèi)存空間)
3、線程是由進程創(chuàng)建的(寄生在進程)
4、一個進程可以擁有多個線程-->這就是我們常說的多線程編程
線程的幾種狀態(tài):
a、創(chuàng)建狀態(tài)(new)
b、就緒狀態(tài)(Runnable)
c、運行狀態(tài)(Running)
d、阻塞狀態(tài)(Blocked)
e、死亡狀態(tài)(Dead)
3、多線程:如果在一個進程中同時運行了多個線程,用來完成不同的工作,則稱之為“多線程”;多個線程交替占用CPU資源,而非真正的并行執(zhí)行
●多線程好處:
a)充分利用CPU的資源
b)簡化編程模型
c)帶來良好的用戶體驗
4、JAVA中實現(xiàn)多線程:
●Thread類:Java提供了java.lang.Thread類支持多線程編程
◆ Thread類常用的方法:
構(gòu)造方法
說?????? 明
Thread()
分配新的Thread()對象
Thread(Runnable target)
分配新的Thread()對象。tarage為run()方法被調(diào)用的對象;
Thread(Runnable target,String nasme)
分配新的Thread()對象。tarage為run()方法被調(diào)用的對象;name為新線程的名稱
void run()
執(zhí)行任務(wù)操作的方法
void start()
使該線程開始執(zhí)行,JAVA虛擬機調(diào)用線程的run()方法
static void sleep(long millis)
在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行)
String getName()
返回線程的名字
Int getPriority()
返回線程的優(yōu)先級
void setPriority(int? newPriority)
更改線程的優(yōu)先級
static Thread currentThread()
返回當(dāng)前正在執(zhí)行的線程對象的引用
void join()
等待該線程終止
static void yield()
暫停當(dāng)前正在執(zhí)行的線程對象,并執(zhí)行其他線程
void interrupt()
中斷線程
boolean isAlive()
測試線程是否處于活動狀態(tài)
5、主線程:在JAVA線程啟動時,一個線程立即運行該線程稱為程序的主線程;每個線程至少有一個主線程;他是程序開始時就執(zhí)行;
◆?main()方法即為主線程入口
◆產(chǎn)生其他子線程的線程
◆必須最后完成執(zhí)行,因為它執(zhí)行各種關(guān)閉動作
★主線程可以由一個Thread對象控制,需要調(diào)用方法Thread.currentThread()獲得他的一個引用,
語法:static Thread currentThread()
Eg:
public static void main(String args[]) {
Thread t= Thread.currentThread(); //獲得主線程對象
System.out.println("當(dāng)前線程是: "+t.getName());
t.setName("MyJavaThread"); //設(shè)置線程名
System.out.println("當(dāng)前線程名是: "+t.getName()); //獲取線程名
}
◆在Java中創(chuàng)建線程的兩種方式:
★繼承java.lang.Thread類:繼承Thread類,并重寫run函數(shù)
★實現(xiàn)java.lang.Runnable接口:實現(xiàn)Runnable接口,并重寫run函數(shù)
◆使用線程的步驟:
★定義一個線程:同時指明這個線程所要執(zhí)行的代碼;
★創(chuàng)建線程對象;
★啟動線程
★終止線程
6、繼承Thread類創(chuàng)建線程:
●步驟:
★定義MyThread類繼承Thread類
★重寫run()方法,編寫線程執(zhí)行體
★創(chuàng)建線程對象,調(diào)用start()方法啟動線程
Eg:
public class MyThread extends Thread{? ? //繼承Thread類
//重寫run()方法
public void run(){? ? ? ? ?//run()方法中編寫線程執(zhí)行的代碼
for(int i=1;i<100;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); //啟動線程
}
多個線程交替執(zhí)行,不是真正的“并行”
線程每次執(zhí)行時長由分配的CPU時間片長度決定
Eg:MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
直接調(diào)用run()和start()區(qū)別:
start():啟動線程;
run():調(diào)用實例方法;
7、實現(xiàn)Runnable接口創(chuàng)建線程:
●步驟:
★定義MyRunnable類實現(xiàn)Runnable接口,并實現(xiàn)Runnable接口的run()方法在run()方法中實現(xiàn)輸出數(shù)據(jù);
★創(chuàng)建MyRunnable類的對象myRunnable;
★創(chuàng)建一個Thread類的對象myThread,將myRunnable對象作為Thread類構(gòu)造方法的參數(shù)傳入;
★調(diào)用myThread對象的start()方法啟動線程;
Eg:
public class MyRunnable implements Runnable{?? //實現(xiàn)Runnable接口
public void run(){
for(int i=1;i<100;i++){?????????? //?? run()方法中編寫線程執(zhí)行的代碼
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();? //創(chuàng)建線程對象
Thread myThread = new Thread(myRunnable);
thread.start(); ??//啟動線程
}
■用實現(xiàn)Runnable接口的特點:
1、用實現(xiàn)Runnable接口的方法創(chuàng)建對象可以避免java單繼承機制帶來的局限;
2、用實現(xiàn)Runnable接口的方法,可以實現(xiàn)多個線程共享同一段代碼(數(shù)據(jù));因此建議大家如果你的程序有同步邏輯需求,則使用Runnable的方法來創(chuàng)建線程。
比較兩種創(chuàng)建線程的方式:推薦使用實現(xiàn)Runnable接口方式創(chuàng)建線程
★繼承Thread類:
編寫簡單,可直接操作線程
適用于單繼承
★實現(xiàn)Runnable接口:
避免單繼承局限性
便于共享資源
附加:從java的設(shè)計來看,通過繼承Thread或者實現(xiàn)Runnable接口來創(chuàng)建線程本質(zhì)上沒有區(qū)別,
★從jdk幫助文檔我們可以看到Thread類本身就實現(xiàn)了Runnable接口,區(qū)別如下:
1、盡可能使用實現(xiàn)Runnable接口的方式來創(chuàng)建線程
2、在使用Thread的時候只需要new一個實例出來,調(diào)用start()方法即可以啟動一個線程,
如:????? Thread test=new Thread();
test.start();
3、在使用Runnable的時候需要先new一個實現(xiàn)Runnable的實例,之后用Thread調(diào)用,如:
Test implements Runnable
Test t=new Test();
Thread test=new Thread(t);
tset.start();
注意:不管是通過繼承Thread,還是通過實現(xiàn)Runnable接口創(chuàng)建線程,它們的一個對象只能啟動(即:start())一次。否則就會有(IllegaiIThreadStateException)異常拋出。
8、線程的狀態(tài):
1. 創(chuàng)建狀態(tài):在程序中用構(gòu)造方法創(chuàng)建了一個線程對象后,新的線程對象就處于創(chuàng)建狀態(tài),此時,它已經(jīng)獲取了相應(yīng)的資源,但還沒有處于可運行狀態(tài),這時可以通過Thread類的方法來設(shè)置線程對象的屬性,
如:設(shè)置線程名(setName())、設(shè)置線程優(yōu)先級(setPriority())等。
2. 就緒狀態(tài):線程創(chuàng)建之后,就可以通過調(diào)用start()方法啟動線程,即進入就緒狀態(tài)。此時,線程將進入線程隊列排隊,等待CPU資源,這表明它已經(jīng)具備了運行條件,在未獲得CPU資源時,仍不能真正執(zhí)行。
◆舉例來說,去醫(yī)院看病,某主任的專家號每天只有20個,掛上號的病人還需在分診處等待叫號。這里每個掛到專家號的病人可以看成一個就緒狀態(tài)的線程。
3.運行狀態(tài):當(dāng)就緒狀態(tài)的線程獲得CPU資源時,即可轉(zhuǎn)入運行狀態(tài),執(zhí)行的run()方法。對于只有一個CPU的機器而言,任何時刻只能有一個處于運行狀態(tài)的線程占用CPU,即獲得CPU資源。
◆延續(xù)上面醫(yī)院看病的例子,被叫到的病人才能真正就診,而每個主任專家在一個時刻只能為一個病人看病。
4. 阻塞狀態(tài):一個正在運行的線程因某種原因不能繼承運行時,進入阻塞狀態(tài)。阻塞狀態(tài)是一種“不可運行”的狀態(tài),而處于這種狀態(tài)的線程在得到一個特定的事件之后會轉(zhuǎn)回可運行狀態(tài)。
◆舉例來說,輪到小張看病了,醫(yī)生為查明原因要求他去做個化驗,醫(yī)生得到化驗結(jié)果后才能繼續(xù)診斷,如果把醫(yī)生給小張看病看作一個線程,該線程此時即處于阻塞狀態(tài)。
●可能使線程暫停執(zhí)行的條件:
?★線程優(yōu)先級比較低,因此它不能獲得CPU資源。
?★使用sleep()方法使線程休眠。
?★通過調(diào)用wait()方法,使線程等待。
?★通過調(diào)用yield()方法,線程顯式出讓CPU控制權(quán)。
?★線程由于等待一個文件I/O事件被阻塞。
5.死亡狀態(tài):一個線程的run()方法運行完畢,線程則進入死亡狀態(tài)。處于死亡狀態(tài)的線程不具有繼承運行的能力。
9、線程調(diào)度:線程調(diào)度指按照特定機制為多個線程分配CPU的使用權(quán);
●線程優(yōu)先級:線程靠搶CPU時間片而執(zhí)行,誰搶的多誰利用CPU的時間就多也就執(zhí)行得快。而決定這個爭搶能力的就是線程的優(yōu)先級;
●線程優(yōu)先級由1~10表示,1最低,10代表優(yōu)先級最高,默認(rèn)優(yōu)先級為5。
這些優(yōu)先級對應(yīng)一個Thread類的公用靜態(tài)常量;優(yōu)先級高的線程獲得CPU資源的概率較大;
線程的優(yōu)先級可以通過getPriority()方法獲取,setPriority(int grade)方法更改,參數(shù)表示要設(shè)置的優(yōu)先級,他必須是1~10的整數(shù);
MAX_PRORITY(最大優(yōu)先級),MIN_PRORITY(最小優(yōu)先級),NORM_PRORITY(默認(rèn)優(yōu)先級);
●線程調(diào)度的方法:
方法
說?????? 明
setPriority(int? newPriority)
更改線程的優(yōu)先級
static void sleep(long millis)
在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠
void join()
等待該線程終止
static void yield()
暫停當(dāng)前正在執(zhí)行的線程對象,并行其他線程
void interrupt()
中斷線程
boolean isAlive()
測試線程是否處于活動狀態(tài)
●線程休眠:在一個程序中一個線程允許進行暫時休眠,直接調(diào)用Thread sleep()方法即可實現(xiàn)線程的休眠;
sleep()方法語法定義:public static void sleep(long millis)
◆讓線程暫時睡眠指定時長,線程進入阻塞狀態(tài);
◆睡眠時間過后線程會再進入可運行狀態(tài);
Eg: public class Wait {
public static void bySec(long s) {
for (int i = 0; i < s; i++) {
System.out.println(i + 1 + "秒");
try {
Thread.sleep(1000); //線程休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}}}}
★millis為休眠時長,以毫秒為單位
★調(diào)用sleep()方法需處理InterruptedException異常
●線程的強制運行:使當(dāng)前線程暫停執(zhí)行,等待其他線程結(jié)束后再繼續(xù)執(zhí)行本線程:
它的三個重載語法語法:
public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)
◆millis:以毫秒為單位的等待時長
◆nanos:要等待的附加納秒時長
◆需處理InterruptedException異常
Eg:
public static void main(String[] args) {
Thread temp = new Thread(new MyThread());
temp.start();
for(int i=0;i<20;i++){
if(i==5){
try {
temp.join();//阻塞主線程,子線程強制執(zhí)行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"運行:"+i);
}
//省略代碼……
}
結(jié)果:
●線程的禮讓:
◆yield()方法可以暫停當(dāng)前線程,允許其他具有相同優(yōu)先級的線程獲得運行機會;
◆該線程處于就緒狀態(tài),不轉(zhuǎn)為阻塞狀態(tài);此時系統(tǒng)選擇其他想相同或更高優(yōu)先級線程允許,若無其他相同或更高優(yōu)先級線程,則該線程繼續(xù)運行;
語法:public static void yield()
注:使用yield()的線程禮讓只是提供一種可能,但是不能保證一定會實現(xiàn)禮讓;
Eg:
10、線程的同步:當(dāng)多個線程共同操縱同一個共享資源的時候,會導(dǎo)致數(shù)據(jù)不一致的情況;當(dāng)多個線程共同操縱同一個共享資源的時候,一個線程未完成操作的時候,其他線程修改的數(shù)據(jù),會導(dǎo)致數(shù)據(jù)不安全的情況;
●多線程共享數(shù)據(jù)引發(fā)的問題:
●同步方法:
◆使用synchronized修飾的方法控制對類成員變量的訪問
語法:
◆synchronized(同步關(guān)鍵字)就是為當(dāng)前的線程聲明一個鎖
◆?syncObject為需同步的對象,通常為this,效果與同步方法相同
◆多個并發(fā)線程訪問同一資源的同步代碼塊時注意:
1、當(dāng)多個并發(fā)線程訪問同一個對象object的synchronized(this)同步代碼塊時,同一時刻只能有一個線程得到執(zhí)行,其他線程必須等待當(dāng)前線程執(zhí)行完畢之后才能執(zhí)行該代碼塊;
2、當(dāng)一個線程訪問一個object的synchronized(this)同步代碼塊時,其他線程對object中所有其他synchronized(this)同步代碼塊的訪問被阻塞,即該線程獲得這個object的對象鎖,其他線程對該object對象所有同步代碼部分的訪問被暫時阻塞;
3、當(dāng)一個線程訪問一個object的synchronized(this)同步代碼塊時,其他線程可以訪問該資源的非synchronized(this)同步代碼;
綜上所述:synchronized就是為當(dāng)前線程聲明一個鎖,獲得這個鎖的線程可以執(zhí)行代碼塊里的指令,其他的線程只能等待解鎖,然后才能執(zhí)行相同的操作;
◆同步代碼塊的應(yīng)用場景:假設(shè)某線程是非線程安全的,而且該類是第三方創(chuàng)建的或者是從內(nèi)置庫導(dǎo)入的,所以不能獲取他的源代碼,這樣,無法在相關(guān)方法前面加synchronized修飾符;
怎樣使該類的一個對象同步化?
解決方案:只需將對調(diào)用該方法的代碼放入一個synchronized塊內(nèi)就可以了;
◆關(guān)鍵代碼:
synchronized(同步對象){
//調(diào)用第三方非同步方法的代碼
}
11、線程安全的類型:
查看ArrayList類的add()方法定義:
public boolean add(E e) {
ensureCapacityInternal(size + 1);// 集合擴容,確保能新增數(shù)據(jù)
elementData[size++] = e;?? //在新增位置存放數(shù)據(jù)
return true;
}
ArrayList類的add()方法為非同步方法;
當(dāng)多個線程向同一個ArrayList對象添加數(shù)據(jù)時,可能出現(xiàn)數(shù)據(jù)不一致問題
ArrayList為非線程安全的類型
方法是否同步
效率比較
適合場景
線程安全(被synchronized修飾)
是
低
多線程并發(fā)共享資源
非線程安全
否
高
單線程
為達到安全性和效率的平衡,可以根據(jù)實際場景來選擇合適的類型
12、常用類型對比:
◆Hashtable&&HashMap
◆Hashtable:繼承關(guān)系,實現(xiàn)了Map接口,Hashtable繼承Dictionary類
特點:線程安全,效率較低
鍵和值都不允許為null
◆HashMap:繼承關(guān)系:實現(xiàn)了Map接口,繼承AbstractMap類
特點:?非線程安全,效率較高
鍵和值都允許為null
◆?StringBuffer? &&? StringBuilder:前者線程安全,后者非線程安全
13、附加1:死鎖問題:
*死鎖:兩個線程都在等待對方先完成,造成程序的停滯;
*產(chǎn)生死鎖的條件:
*兩個或兩個以上的線程在活動;
*某個線程拿到一個鎖以后,還想拿第二個鎖,造成鎖的嵌套?
*解決方法:
*當(dāng)前線程先釋放自己的鎖?
*盡量減少同步方法或同步代碼塊的嵌套?
Eg:兩個小孩互換玩具;
Eg:此示例不會產(chǎn)生死鎖;
/**
* 模擬死鎖
*/
public class Test {
public static void main(String[] args) {
Thread tang=new Thread(new Tang());
Thread dou=new Thread(new Dou());
tang.start();
dou.start();
}
}
/**
* 堂堂
*/
class Tang implements Runnable{
Object bobby=new Object();//芭比娃娃
Object duck=new Object();//玩具
@Override
public void run() {
synchronized (bobby) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (duck) {
}
System.out.println("堂堂把芭比娃娃給豆豆玩!");
}
}
}
/**
* 豆豆
*/
class Dou implements Runnable{
Object bobby=new Object();//芭比娃娃
Object duck=new Object();//玩具
@Override
public void run() {
synchronized (duck) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (bobby) {
}
System.out.println("豆豆把玩具鴨給堂堂玩");
}
}
}
Eg:此示例產(chǎn)生了死鎖
/**
* 模擬死鎖(產(chǎn)生了死鎖)
*/
public class Test1 {
public static void main(String[] args) {
Object bobby=new Object();
Object duck=new Object();
Thread tang=new Thread(new Tangtang(bobby,duck));
Thread dou=new Thread(new Doudou(bobby,duck));
tang.start();
dou.start();
}
}
/**
* 堂堂
*/
class Tangtang implements Runnable{
Object bobby;//芭比娃娃
Object duck;//玩具
//構(gòu)造函數(shù)
public Tangtang(Object bobby,Object duck) {
super();
this.bobby=bobby;
this.duck=duck;
}
@Override
public void run() {
synchronized (bobby) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (duck) {
}
System.out.println("堂堂把芭比娃娃給豆豆玩!");
}
}
}
/**
* 豆豆
*/
class Doudou implements Runnable{
Object bobby;//芭比娃娃
Object duck;//玩具
//構(gòu)造函數(shù)
public Doudou(Object bobby,Object duck) {
super();
this.bobby=bobby;
this.duck=duck;
}
@Override
public void run() {
synchronized (duck) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (bobby) {
}
System.out.println("豆豆把玩具鴨給堂堂玩");
}
}
}
14、附加2:生產(chǎn)者和消費者的問題: 生產(chǎn)者不斷生產(chǎn),消費者不斷取走生產(chǎn)者的產(chǎn)品;生產(chǎn)者生產(chǎn)出信息之后將其放到一個區(qū)域中,之后消費者從此區(qū)域取走數(shù)據(jù);
Eg:一個輪流錄入電影和讀取電影信息的程序
變形金剛——一部科幻電影
神偷奶爸——一部3D動畫片
分析: 生產(chǎn)者:錄入信息;
消費者:讀取信息;
/**
* 電影類
*/
public class Movie {
private String name;//電影的名稱
private String info;//電影的描述
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
/**
* 生產(chǎn)者
*/
public class Producer implements Runnable {
private Movie movie=null;
private boolean flag=false;
//構(gòu)造函數(shù)
public Producer(Movie movie) {
super();
this.movie=movie;
}
@Override
public void run() {
//循環(huán)錄入50遍電影數(shù)據(jù),兩部電影交替錄入
for (int i = 0; i < 50; i++) {
if(flag) {
this.movie.setName("變形金剛");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.movie.setInfo("一部科幻電影!");
flag=false;
}else {
this.movie.setName("神偷奶爸");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.movie.setInfo("一部3D動畫電影");
flag=true;
}
}
}
}
/**
* 測試類
*/
public class Test {
public static void main(String[] args) {
Movie movie=new Movie();
//生產(chǎn)者線程對象
Thread producer=new Thread(new Producer(movie));
//消費者線程對象
Thread consumer=new Thread(new Consumer(movie)); ?? producer.start();
consumer.start();
}
}
/**
* 消費者
*/
public class Consumer implements Runnable {
private Movie movie=null;
//構(gòu)造函數(shù)
public Consumer(Movie movie) {
super();
this.movie=movie;
}
@Override
public void run() {
//循環(huán)顯示50次電影信息
for (int i = 0; i <50; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.movie.getName()+"—
"+this.movie.getInfo());
}
}
}
輸出結(jié)果:(會出現(xiàn)電影名與類型匹配錯誤的情況(生產(chǎn)與消費不同步))
/**
* 生產(chǎn)者
*/
public class Producer implements Runnable {
private Movie movie=null;
private boolean flag=false;
//構(gòu)造函數(shù)
public Producer(Movie movie) {
super();
this.movie=movie;
}
@Override
public void run() {
//循環(huán)錄入50遍電影數(shù)據(jù),兩部電影交替錄入
for (int i = 0; i < 50; i++) {
if(flag) {
this.movie.set("變形金剛", "一部科幻電影");
flag=false;
}else {
this.movie.set("神偷奶爸", "一部3D動畫電影");
flag=true;
}
}
}
}
/**
* 消費者
*/
public class Consumer implements Runnable {
private Movie movie=null;
//構(gòu)造函數(shù)
public Consumer(Movie movie) {
super();
this.movie=movie;
}
@Override
public void run() {
//循環(huán)顯示50次電影信息
for (int i = 0; i <50; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.movie.get();
}
}
}
package lianxi0Producer;
/**
* 電影類
*/
public class Movie {
private String name;//電影的名稱
private String info;//電影的描述
public String getName() {
return name;
}
public String getInfo() {
return info;
}
//同步寫入
public synchronized void set(String name,String info) {
this.name=name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info=info;
}
//同步獲取
public synchronized void get() {
System.out.println(this.getName()+"--"+this.getInfo());
}
}
/**
* 測試類
*/
public class Test {
public static void main(String[] args) {
Movie movie=new Movie();
//生產(chǎn)者線程對象
Thread producer=new Thread(new Producer(movie));
//消費者線程對象
Thread consumer=new Thread(new Consumer(movie));? ?? producer.start();
consumer.start();
}
}
輸出結(jié)果:(沒有實現(xiàn)交替輸出(需要調(diào)用Object類的兩個方法))
notify()喚醒正在等待對象監(jiān)視器的單個線程。
Wait():導(dǎo)致當(dāng)前線程等待,直到另一個線程調(diào)用該對象的 notify()方法或 notifyAll()方法。
/**
* 生產(chǎn)者
*/
public class Producer implements Runnable {
private Movie movie=null;
private boolean flag=false;
//構(gòu)造函數(shù)
public Producer(Movie movie) {
super();
this.movie=movie;
}
@Override
public void run() {
//循環(huán)錄入50遍電影數(shù)據(jù),兩部電影交替錄入
for (int i = 0; i < 50; i++) {
if(flag) {
this.movie.set("變形金剛", "一部科幻電影");
flag=false;
}else {
this.movie.set("神偷奶爸", "一部3D動畫電影");
flag=true;
}
}
}
}
/**
* 消費者
*/
public class Consumer implements Runnable {
private Movie movie=null;
//構(gòu)造函數(shù)
public Consumer(Movie movie) {
super();
this.movie=movie;
}
@Override
public void run() {
//循環(huán)顯示50次電影信息
for (int i = 0; i <50; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.movie.get();
}
}
}
/**
* 電影類
*/
public class Movie {
private String name;//電影的名稱
private String info;//電影的描述
private boolean flag=true;//設(shè)置標(biāo)志位置,控制生產(chǎn)者生產(chǎn),消費者消費
public String getName() {
return name;
}
public String getInfo() {
return info;
}
//同步寫入
public synchronized void set(String name,String info) {
if(!flag) {
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name=name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info=info;
flag=false;//重置標(biāo)志位,讓消費者消費
super.notify();
}
//同步獲取
public synchronized void get() {
if(flag) {
try {
super.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(this.getName()+"—
"+this.getInfo());
flag=true;
super.notify();
}
}
/**
* 測試類
*/
public class Test {
public static void main(String[] args) {
Movie movie=new Movie();
//生產(chǎn)者線程對象
Thread producer=new Thread(new Producer(movie));
//消費者線程對象
Thread consumer=new Thread(new Consumer(movie));???? producer.start();
consumer.start();
}
}
輸出結(jié)果:
15、附加:線程池:
●使用線程池的理由:
◆線程缺乏統(tǒng)一管理,占用過多的系統(tǒng)資源;
◆缺乏更多的功能,如:定期執(zhí)行、定時執(zhí)行等;
●線程池的優(yōu)點:
◆重用存在的線程、減少對象的創(chuàng)建、消亡的開銷;
◆有效控制最大并發(fā)數(shù),提高系統(tǒng)資源使用率;
◆定時執(zhí)行、定期執(zhí)行;
●線程池所在的包:java.util.concurrent;? ?:頂級接口是Executor,真正的線程池接口是ExecutorService;
Java.util.concurrent.Executors類提供創(chuàng)建線程池的方法;
方法名
說明
newCachedThreadPool()
創(chuàng)建一個可緩存的線程池,有任務(wù)時才創(chuàng)建新任務(wù);
newSingleThreadExecutor()
創(chuàng)建一個單線程池
newFixedThreadPool(int nThreads)
創(chuàng)建一個固定長度的線程池,空閑線程池會一直保留,參數(shù)nThreads設(shè)定線程池中線程的數(shù)目
newScheaduledThreadPool(int corePoolSize)
創(chuàng)建了一個固定長度的線程池,而且以延遲或定時的方式來執(zhí)行任務(wù);
●使用newCacherThreadPool()方法創(chuàng)建線程池1:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 使用newCacherThreadPool()方法創(chuàng)建線程池
* @author 逆風(fēng)?飛翔
*
*/
public class Test {
public static void main(String[] args) {
//創(chuàng)建一個線程池
ExecutorService cachedThreadPool= Executors.newCachedThreadPool();
//在線程池中執(zhí)行10個任務(wù)
for (int i = 0; i < 10; i++) {
cachedThreadPool.execute(new MyRunnable(i));
try {
Thread.sleep(1000); //每執(zhí)行一次休眠一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class MyRunnable implements Runnable{
int num;
public MyRunnable(int num) {
super();
this.num=num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+num);
}
}
輸出結(jié)果:pool代表線程池
(10個任務(wù)被同一個線程執(zhí)行)
●使用newCacherThreadPool()方法創(chuàng)建線程池2:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 使用newCacherThreadPool()方法創(chuàng)建線程池
* @author 逆風(fēng)?飛翔
*
*/
public class Test {
public static void main(String[] args) {
//創(chuàng)建一個線程池
ExecutorService cachedThreadPool= Executors.newCachedThreadPool();?? //在線程池中執(zhí)行10個任務(wù)
for (int i = 0; i < 10; i++) {
cachedThreadPool.execute(new MyRunnable(i));
//????? try {
//???????? Thread.sleep(1000);
//????? } catch (InterruptedException e) {
//???????? e.printStackTrace();
//????? }
}
}
}
class MyRunnable implements Runnable{
int num;
public MyRunnable(int num) {
super();
this.num=num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+num);
}
}
結(jié)果:(更多的線程參與執(zhí)行,有些線程執(zhí)行了兩次,
eg:線程8執(zhí)行完7后,又繼續(xù)執(zhí)行了9,
省去創(chuàng)建一個新的線程,當(dāng)有一個線程閑置下來后不需要去銷毀,又去執(zhí)行了一個新的任務(wù))
●使用newSingleThreadExecutor()方法創(chuàng)建單線程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 使用newSingleThreadExecutor()方法創(chuàng)建單線程池
*/
public class Test1 {
public static void main(String[] args) {
//創(chuàng)建一個單線程池
ExecutorService singleThreadPool= Executors.newSingleThreadExecutor();
//在線程池中執(zhí)行10個任務(wù)
for (int i = 0; i < 10; i++) {
singleThreadPool.execute(new MyRunnable(i));
//????? try {
//???????? Thread.sleep(1000);
//????? } catch (InterruptedException e) {
//???????? e.printStackTrace();
//????? }
}
}
}
class MyRunnable implements Runnable{
int num;
public MyRunnable(int num) {
super();
this.num=num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+num);
}
}
輸出結(jié)果:
●使用newFixedThreadExecutor()方法創(chuàng)建指定個數(shù)線程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 使用newFixedThreadExecutor()方法創(chuàng)建單線程池
*/
public class Test2 {
public static void main(String[] args) {
//創(chuàng)建一個線程池
ExecutorService fixedThreadPool= Executors.newFixedThreadPool(3);
//在線程池中執(zhí)行10個任務(wù)
for (int i = 0; i < 10; i++) {
fixedThreadPool.execute(new McRunnable(i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class McRunnable implements Runnable{
int num;
public McRunnable(int num) {
super();
this.num=num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+":"+num);
}
}
輸出結(jié)果:(只有三個線程在執(zhí)行)
●
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 使用newSchediledThreadPool()方法來創(chuàng)建線程池
*/
public class Test3 {
public static void main(String[] args) {
//創(chuàng)建三個線程
ScheduledExecutorService scheduledThreadPool=Executors.newScheduledThreadPool(3);
System.out.println("*************開始執(zhí)行*************");
scheduledThreadPool.scheduleAtFixedRate(new MdRunnable(), 5, 2, TimeUnit.SECONDS);
}
}
class MdRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"延時5s執(zhí)行,每2s執(zhí)行一次");
}
}
●ThreadPoolExecutor類
◆構(gòu)造器中各個參數(shù)的含義:
★corePoolSize:核心池的大小;
★maximumPoolSize:線程池最大線程數(shù);
★keepAliveTime:表示線程沒有任務(wù)執(zhí)行時最多保持多久時間會終止;
★unit:參數(shù)keepAliveTime的時間單位;
★workQueue:一個阻塞隊列,用來存儲等待執(zhí)行的任務(wù);
★threadFactory:線程工廠,主要用來創(chuàng)建線程;
★handler:表示當(dāng)拒絕處理任務(wù)時的策略;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 創(chuàng)建自定義線程池
*/
public class Test4 {
public static void main(String[] args) {
//創(chuàng)建一個自定義線程池(核心線程數(shù),最大線程數(shù),超出的線程消亡的時間,指定毫秒數(shù),阻塞隊列)
ThreadPoolExecutor executor=new ThreadPoolExecutor(5, 7, 3000,
TimeUnit.MICROSECONDS, new ArrayBlockingQueue(4));
//執(zhí)行12個任務(wù)
for (int i = 1; i <=12; i++) {
//i為傳入當(dāng)前執(zhí)行的數(shù)量
executor.execute(new MmRunnable(i));
System.out.println("線程池中的線程數(shù):"+executor.getPoolSize()+
",隊列中等待執(zhí)行的任務(wù):"+executor.getQueue().size()+
",已執(zhí)行完成的任務(wù)數(shù):"+executor.getCompletedTaskCount());
}
executor.shutdown();//關(guān)閉線程池
}
}
class MmRunnable implements Runnable{
int num;//第幾個任務(wù)
public MmRunnable(int num) {
super();
this.num=num;
}
@Override
public void run() {
System.out.println("正在執(zhí)行任務(wù)"+num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("任務(wù)"+num+"執(zhí)行完畢!");
}
}
運行結(jié)果:
(會創(chuàng)建第12個線程所以會報錯)
16、附加:
●進程和線程的基本概念和原理:
一個進程至少包含一個線程,即主線程。
多個線程的執(zhí)行時間是由CPU的時間片決定的,時長是隨機的。
在單CPU計算機中,一個進程的多個線程是交替執(zhí)行的,在某一時刻,只能執(zhí)行一個線程。
●線程是進程的一個執(zhí)行路徑,而不是一個程序,
線程是進程中執(zhí)行運算的最小單位,
多線程用于實現(xiàn)并發(fā),
單CPU的計算機中,CPU每個時刻只能執(zhí)行一條指令,將CPU的執(zhí)行時間分成多個時間片,分配給不同的線程。
●線程的創(chuàng)建和啟動。當(dāng)線程被創(chuàng)建后,執(zhí)行start()方法后,線程處于可運行狀態(tài)。
當(dāng)線程執(zhí)行完一個時間片后繼續(xù)回到就緒狀態(tài),等待CPU再次分配時間片,而不存在一個隊列。
線程休眠后,處于阻塞狀態(tài)。
●線程的創(chuàng)建和啟動,線程類的定義有繼承Thead類和實現(xiàn)Runable接口兩種方式,
通過實現(xiàn)run()方法編寫線程體,
已啟動的線程不能重復(fù)調(diào)用start()方法,否則會報IllegalThreadStateException異常。
●當(dāng)線程對象調(diào)用start()方法后,進入就緒狀態(tài),
當(dāng)CPU分配時間片后即可執(zhí)行,可以實現(xiàn)多線程并發(fā),
而直接調(diào)用run()方法和調(diào)用普通方法一樣,只有主線程一條執(zhí)行路徑,不能實現(xiàn)并發(fā)執(zhí)行。
●eg:
分析:
本題考察線程的狀態(tài)。創(chuàng)建一個線程對象后,線程對象處于新建狀態(tài),執(zhí)行start()方法后,處于就緒狀態(tài)。休眠時處于阻塞狀態(tài),休眠后處于就緒狀態(tài),執(zhí)行輸出語句時處于執(zhí)行狀態(tài)。
Eg:
分析:
本題主要考查線程的強制執(zhí)行。當(dāng)主線程 main方法執(zhí)行System.out.println(a);這條語句時,線程還沒有真正開始運行,或許正在為它分配資源準(zhǔn)備運行。因為為線程分配資源需要時間,而main方法執(zhí)行完t.start()方法后繼續(xù)往下執(zhí)行System.out.println(a);,這個時候得到的結(jié)果是a還沒有被改變的值0 。怎樣才能讓輸出結(jié)果為5, join() 方法提供了這種功能。它能夠使調(diào)用該方法的線程在此之前執(zhí)行完畢。
●eg:
分析:本題考察線程的創(chuàng)建。在自定義線程中如果沒有重寫run()方法,則默認(rèn)執(zhí)行父類Thread中的run()方法,因此不會出現(xiàn)編譯錯誤,也無任務(wù)輸出結(jié)果,
●Eg:
分析:本題考察如何獲取當(dāng)前線程。線程對象mt直接調(diào)用了run()方法,沒有使用start()方法啟動線程。因此本題目中只啟動了主線程和new Thread(mt,"線程"),主線程的線程名為main.
Eg:
分析:sleep()方法,它不能終止一個線程,它是將線程睡眠一段時間。當(dāng)時間結(jié)束以后,線程就會再次啟動。它是不會交出線程鎖的
Eg:
分析:
因為currentThread()是靜態(tài)方法,使用類名可直接調(diào)用。
Eg:
分析:
本題考察線程安全的類型。當(dāng)多個線程共同操作Counter類的成員變量count時,會引出線程安全問題。解決問題的辦法就是對addCount()方法加鎖。如果將count變量定義在addCount()方法中,就不會存在多線程共同操作的數(shù)據(jù),不會引發(fā)并發(fā)問題,本題答案為bc
Eg;
D:一個類中可以有多個同步方法或同步代碼塊;
●synchronized用來定義同步方法或同步代碼塊,解決多個線程共享資源時帶來的問題,同步后,只允許一個線程進入同步方法或同步代碼塊。
對于synchronized關(guān)鍵字最精準(zhǔn)的描述:保證在某個時刻只有一個線程可以訪問方法或?qū)ο?#xff1b;
●synchronized是Java中的關(guān)鍵字,是一種同步鎖。它修飾的對象有以下幾種:
1.修飾一個代碼塊,被修飾的代碼塊稱為同步語句塊,其作用的范圍是大括號{}括起來的代碼,作用的對象是調(diào)用這個代碼塊的對象;
2.修飾一個方法,被修飾的方法稱為同步方法,其作用的范圍是整個方法,作用的對象是調(diào)用這個方法的對象;
3.修改一個靜態(tài)的方法,其作用的范圍是整個靜態(tài)方法,作用的對象是這個類的所有對象;
4.修改一個類,其作用的范圍是synchronized后面括號括起來的部分,作用主的對象是這個類的所有對象。
版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點,本站所提供的攝影照片,插畫,設(shè)計作品,如需使用,請與原作者聯(lián)系,版權(quán)歸原作者所有
總結(jié)
以上是生活随笔為你收集整理的java获取其他类的线程,使用Java实现面向对象编程——第七章 多线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 联想Z500拆解清灰教程联想电脑如何清灰
- 下一篇: java美元兑换,(Java实现) 美元