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