Java線程之多線程與多進程(3)——Java中的多線程

單線程

任何程序至少有一個線程,即使你沒有主動地創建線程,程序從一開始執行就有一個默認的線程,被稱為主線程,只有一個線程的程序稱為單線程程序。如下面這一簡單的代碼,沒有顯示地創建一個線程,程序從main開始執行,main本身就是一個線程(主線程),單個線程從頭執行到尾。

public class Test{public static void main(String args[]) {System.out.println("1-100:");for (int i = 0; i < 100; i ++) {System.out.println(i + 1);}}
}

創建線程

單線程程序簡單明了,但有時無法滿足特定的需求。如一個文字處理的程序,我在打印文章的同時也要能對文字進行編輯,如果是單線程的程序則要等打印機打印完成之后你才能對文字進行編輯,但打印的過程一般比較漫長,這是我們無法容忍的。如果采用多線程,打印的時候可以單獨開一個線程去打印,主線程可以繼續進行文字編輯。在程序需要同時執行多個任務時,可以采用多線程。

在程序需要同時執行多個任務時,可以采用多線程。Java給多線程編程提供了內置的支持,提供了兩種創建線程方法:1.通過實現Runable接口;2.通過繼承Thread類。

Thread是JDK實現的對線程支持的類,Thread類本身實現了Runnable接口,所以Runnable是顯示創建線程必須實現的接口; Runnable只有一個run方法,所以不管通過哪種方式創建線程,都必須實現run方法。我們來看一個例子。

【Demo2】:線程的創建和使用

//通過實現Runnable方法 
class ThreadA implements Runnable {private Thread thread;private String threadName;public ThreadA(String threadName) {thread = new Thread(this, threadName);this.threadName = threadName;}//實現run方法public void run() {for (int i = 0; i < 10; i ++) {System.out.println(threadName + ": " + i);}}public void start() {thread.start();}
}//通過繼承Thread類
class ThreadB extends Thread {private String threadName;public ThreadB(String threadName) {super(threadName);this.threadName = threadName;}//實現run方法public void run() {for (int i = 0; i < 10; i ++) {System.out.println(threadName + ": " + i);}}
}public class Test{public static void main(String args[]) {ThreadA threadA = new ThreadA("ThreadA");ThreadB threadB = new ThreadB("ThreadB");threadA.start();threadB.start();}
}

運行結果:

說明:上面的例子中例舉了兩種實現線程的方式。大部分情況下選擇實現Runnable接口的方式會優于繼承Thread的方式,因為:?

1. 從 Thread 類繼承會強加類層次;?

2. 有些類不能繼承Thread類,如要作為線程運行的類已經是某一個類的子類了,但Java只支持單繼承,所以不能再繼承Thread類了。

--------------------------------------------------------------------------------------------------------------------------------------------

線程同步

線程與線程之間的關系,有幾種:

模型一:簡單的線程,多個線程同時執行,但各個線程處理的任務毫不相干,沒有數據和資源的共享,不會出現爭搶資源的情況。這種情況下不管有多少個線程同時執行都是安全的,其執行模型如下:?

處理相互獨立的任務?
圖 1:處理相互獨立的任務

型二:復雜的線程,多個線程共享相同的數據或資源,就會出現多個線程爭搶一個資源的情況。這時就容易造成數據的非預期(錯誤)處理,是線程不安全的,其模型如下:?

多個線程共享相同的數據或資源?
圖 2:多個線程共享相同的數據或資源

在出現模型二的情況時就要考慮線程的同步,確保線程的安全。Java中對線程同步的支持,最常見的方式是添加synchronized同步鎖。

我們通過一個例子來看一下線程同步的應用。

買火車票是大家春節回家最為關注的事情,我們就簡單模擬一下火車票的售票系統(為使程序簡單,我們就抽出最簡單的模型進行模擬):有500張從北京到上海的火車票,在8個窗口同時出售,保證系統的穩定性和數據的原子性。?

?

模擬火車票售票系統?

圖 3:模擬火車票售票系統

【Demo3】:火車票售票系統模擬程序
/*** 模擬服務器的類*/
class Service {private String ticketName;    //票名private int totalCount;        //總票數private int remaining;        //剩余票數public Service(String ticketName, int totalCount) {this.ticketName = ticketName;this.totalCount = totalCount;this.remaining = totalCount;}public synchronized int saleTicket(int ticketNum) {if (remaining > 0) {remaining -= ticketNum;try {        //暫停0.1秒,模擬真實系統中復雜計算所用的時間Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}if (remaining >= 0) {return remaining;} else {remaining += ticketNum;return -1;}}return -1;}public synchronized int getRemaining() {return remaining;}public String getTicketName() {return this.ticketName;}}/*** 售票程序*/
class TicketSaler implements Runnable {private String name;private Service service;public TicketSaler(String windowName, Service service) {this.name = windowName;this.service = service;}@Overridepublic void run() {while (service.getRemaining() > 0) {synchronized (this){System.out.print(Thread.currentThread().getName() + "出售第" + service.getRemaining() + "張票,");int remaining = service.saleTicket(1);if (remaining >= 0) {System.out.println("出票成功!剩余" + remaining + "張票.");} else {System.out.println("出票失敗!該票已售完。");}}}}
}

測試程序:

/*** 測試類*/
public class TicketingSystem {public static void main(String args[]) {Service service = new Service("北京-->上海", 500);TicketSaler ticketSaler = new TicketSaler("售票程序", service);//創建8個線程,以模擬8個窗口Thread threads[] = new Thread[8];for (int i = 0; i < threads.length; i++) {threads[i] = new Thread(ticketSaler, "窗口" + (i + 1));System.out.println("窗口" + (i + 1) + "開始出售 " + service.getTicketName() + " 的票...");threads[i].start();}}
}

結果如下:

窗口1開始出售 北京–>贛州 的票…?
窗口2開始出售 北京–>贛州 的票…?
窗口3開始出售 北京–>贛州 的票…?
窗口4開始出售 北京–>贛州 的票…?
窗口5開始出售 北京–>贛州 的票…?
窗口6開始出售 北京–>贛州 的票…?
窗口7開始出售 北京–>贛州 的票…?
窗口8開始出售 北京–>贛州 的票…?
窗口1出售第500張票,出票成功!剩余499張票.?
窗口1出售第499張票,出票成功!剩余498張票.?
窗口6出售第498張票,出票成功!剩余497張票.?
窗口6出售第497張票,出票成功!剩余496張票.?
窗口1出售第496張票,出票成功!剩余495張票.?
窗口1出售第495張票,出票成功!剩余494張票.?
窗口1出售第494張票,出票成功!剩余493張票.?
窗口2出售第493張票,出票成功!剩余492張票.?
窗口2出售第492張票,出票成功!剩余491張票.?
窗口2出售第491張票,出票成功!剩余490張票.?
窗口2出售第490張票,出票成功!剩余489張票.?
窗口2出售第489張票,出票成功!剩余488張票.?
窗口2出售第488張票,出票成功!剩余487張票.?
窗口6出售第487張票,出票成功!剩余486張票.?
窗口6出售第486張票,出票成功!剩余485張票.?
窗口3出售第485張票,出票成功!剩余484張票.?
……

在上面的例子中,涉及到數據的更改的Service類saleTicket方法和TicketSaler類run方法都用了synchronized同步鎖進行同步處理,以保證數據的準確性和原子性。

關于synchronized更詳細的用法請參見:《Java中Synchronized的用法》


線程控制

在多線程程序中,除了最重要的線程同步外,還有其它的線程控制,如線程的中斷、合并、優先級等。

線程等待(wait、notify、notifyAll)

Wait:使當前的線程處于等待狀態;?
Notify:喚醒其中一個等待線程;?
notifyAll:喚醒所有等待線程。

詳細用法參見:《?Java多線程中wait, notify and notifyAll的使用》


線程中斷(interrupt)

在Java提供的線程支持類Thread中,有三個用于線程中斷的方法:?
1、public void interrupt(); 中斷線程。?
2、public static boolean interrupted(); 是一個靜態方法,用于測試當前線程是否已經中斷,并將線程的中斷狀態 清除。所以如果線程已經中斷,調用兩次interrupted,第二次時會返回false,因為第一次返回true后會清除中斷狀態。?
3、public boolean isInterrupted(); 測試線程是否已經中斷。

【Demo4】:線程中斷的應用

/*** 打印線程*/
class Printer implements Runnable {public void run() {while (!Thread.currentThread().isInterrupted()) {     //如果當前線程未被中斷,則執行打印工作System.out.println(Thread.currentThread().getName() + "打印中… …");}if (Thread.currentThread().isInterrupted()) {System.out.println("interrupted:" +  Thread.interrupted());       //返回當前線程的狀態,并清除狀態System.out.println("isInterrupted:" +  Thread.currentThread().isInterrupted());}}
}

調用代碼:

Printer printer = new Printer();
Thread printerThread = new Thread(printer, "打印線程");
printerThread.start();
try {Thread.sleep(100);
} catch (InterruptedException e) {e.printStackTrace();
}
System.out.println("有緊急任務出現,需中斷打印線程.");
System.out.println("中斷前的狀態:" + printerThread.isInterrupted());
printerThread.interrupt();       // 中斷打印線程
System.out.println("中斷前的狀態:" + printerThread.isInterrupted());

結果:

打印線程打印中… …?
… …?
打印線程打印中… …?
有緊急任務出現,需中斷打印線程.?
打印線程打印中… …?
中斷前的狀態:false?
打印線程打印中… …?
中斷前的狀態:true?
interrupted:true?
isInterrupted:false

線程合并(join)

所謂合并,就是等待其它線程執行完,再執行當前線程,執行起來的效果就好像把其它線程合并到當前線程執行一樣。其執行關系如下:?

線程合并的過程?
圖 4:線程合并的過程

public final void join()?
等待該線程終止

public final void join(long millis);?
等待該線程終止的時間最長為 millis 毫秒。超時為 0 意味著要一直等下去。

public final void join(long millis, int nanos)?
等待該線程終止的時間最長為 millis 毫秒 + nanos 納秒

這個常見的一個應用就是安裝程序,很多大的軟件都會包含多個插件,如果選擇完整安裝,則要等所有的插件都安裝完成才能結束,且插件與插件之間還可能會有依賴關系。

【Demo5】:線程合并

/*** 插件1*/
class Plugin1 implements Runnable {@Overridepublic void run() {System.out.println("插件1開始安裝.");System.out.println("安裝中...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("插件1完成安裝.");}
}/*** 插件2*/
class Plugin2 implements Runnable {@Overridepublic void run() {System.out.println("插件2開始安裝.");System.out.println("安裝中...");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("插件2完成安裝.");}
}

合并線程的調用:

System.out.println("主線程開啟...");
Thread thread1 = new Thread(new Plugin1());
Thread thread2 = new Thread(new Plugin2());
try {thread1.start();   //開始插件1的安裝thread1.join();       //等插件1的安裝線程結束thread2.start();   //再開始插件2的安裝thread2.join();       //等插件2的安裝線程結束,才能回到主線程
} catch (InterruptedException e) {e.printStackTrace();
}
System.out.println("主線程結束,程序安裝完成!");

結果如下:

主線程開啟…?
插件1開始安裝.?
安裝中…?
插件1完成安裝.?
插件2開始安裝.?
安裝中…?
插件2完成安裝.?
主線程結束,程序安裝完成!

優先級(Priority)

線程優先級是指獲得CPU資源的優先程序。優先級高的容易獲得CPU資源,優先級底的較難獲得CPU資源,表現出來的情況就是優先級越高執行的時間越多。

Java中通過getPriority和setPriority方法獲取和設置線程的優先級。Thread類提供了三個表示優先級的常量:MIN_PRIORITY優先級最低,為1;NORM_PRIORITY是正常的優先級;為5,MAX_PRIORITY優先級最高,為10。我們創建線程對象后,如果不顯示的設置優先級的話,默認為5。

【Demo】:線程優先級

/*** 優先級*/
class PriorityThread implements Runnable{@Overridepublic void run() {for (int i = 0; i < 1000; i ++) {System.out.println(Thread.currentThread().getName() + ": " + i);}}
}

調用代碼:

//創建三個線程
Thread thread1 = new Thread(new PriorityThread(), "Thread1");
Thread thread2 = new Thread(new PriorityThread(), "Thread2");
Thread thread3 = new Thread(new PriorityThread(), "Thread3");
//設置優先級
thread1.setPriority(Thread.MAX_PRIORITY);
thread2.setPriority(8);
//開始執行線程
thread3.start();
thread2.start();
thread1.start();

從結果中我們可以看到線程thread1明顯比線程thread3執行的快。


---------------------------------結束--------->僅供學習

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/274019.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/274019.shtml
英文地址,請注明出處:http://en.pswp.cn/news/274019.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

幾種常用控件的使用方法

1.UIActivityIndicatorView的使用 UIActivityIndicatorView *activity[[[UIActivityIndicatorViewalloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]autorelease]; z [activity setFrame:CGRectMake(150,150, 50, 50)]; [self.window addSubview:activ…

Java-正則表達式

什么是正則表達式&#xff1f; 正則表達式(Regular Expression)就是用某種模式去匹配一類字符串的公式。如你要在一篇文章中查找第一個字是“李”最后一個字是“建”的三個字的姓名&#xff0c;即“李*建”&#xff1b;那么“李*建”就是公式&#xff0c;也稱作模式(Pattern)&a…

tab標簽的另一種寫法

<div class"good"><ul><li><span>歌曲精選</span></li><li class"other"><span>MV精選</span></li></ul><div class"music_good"><p><span>丁當</span…

java 中 if與while的區別

if&#xff1a;就是一個判斷的&#xff0c;如果滿足后面的條件就繼續運行if語句里面的東西的&#xff0c;要是不滿足就跳出來&#xff0c;執行else語句或執行下面的語句的 。while&#xff1a;就是循環語句的&#xff0c;當滿足while里面的條件時&#xff0c;就會執行里面的循環…

install yael on the ubuntu 12.04

1. bits/predefs.h no such file or directory ??? sudo apt-get install gcc-multilib 2. sudo gedit /etc/profile PATH$PATH:/usr/local/MATLAB/R2012a/bin source /etc/profile 3.ubuntu 切換gcc 版本 1&#xff09;sudo apt-get install gcc-4.4 g-4.4 g-4.4-multi…

Java 線程多線程編程3---線程同步之生產者與消費者問題

生產者與消費者問題&#xff1a; 第一步&#xff1a;把架子搭起來 package com.zhj.www;public class ProceduerConsumer {public static void main(String[] args) {} }//饅頭實體 class wotou{int id;wotou(int id) {this.id id;}public String toString() {return "wo…

windows 服務實例

參考來源:http://blog.csdn.net/morewindows/article/details/6858216 參考來源: http://hi.baidu.com/tfantasy/item/aefa43d66b470a2b38f6f76c 剩下的都是我自己整理的。 在VS2012中新建一個Windows 服務的項目。然后在解決方案目錄下找到Services1.cs&#xff0c;切換到代碼…

Java 線程多線程編程2---線程同步

來模擬一個死鎖&#xff08;互相等待&#xff09;&#xff1a; TestDeadLock.java package com.zhj.www;public class TestDeadLock implements Runnable {public int flag 1;static Object o1 new Object();static Object o2 new Object();public void run() {System.out.p…

Java網絡編程1---基礎

TCP/IP:事實上的標準 自己編的應用程序&#xff1a;應用層 TCP/UDP層 IP層 物理層 數據封裝&#xff1a;第五層只與第四層打交道。 數據拆封《TCP/IP詳解》網絡底層 IP巨大的貢獻&#xff1a;提供了獨一無二的IP地址。 內網IP&#xff1a;虛假的 子網掩碼&#xff1a;255.255.2…

Java網絡編程2---Socket-TCP編程

Sockct:插座Socket是關于TCP的。 端口號&#xff1a;兩個字節->65536個端口號&#xff0c;一個應用程序占多個端口號&#xff1b; 但是假設一個應用程序占一個端口號&#xff1b;一臺電腦會有65535個應用程序。 自己編寫程序要占用端口號1024以上后的。 80端口&#xff1a;網…

winform綁定多張圖片

開發winform程序的時候經常設計到要顯示多張圖片的問題&#xff0c;其解決思路一般是先遍歷文件夾中的所有圖片&#xff0c;然后再把這些圖片添加到ImageList控件中&#xff0c;最后再綁定顯示出來。這里我們介紹兩種綁定的方法&#xff1a; &#xff08;一&#xff09;動態生成…

Java網絡編程3---Socket-UDP編程

栗子&#xff1a;TestUDPServer.java 服務器端&#xff1a; package com.zhj.www;import java.net.DatagramPacket; import java.net.DatagramSocket;public class TestUDPServer {public static void main(String[] args)throws Exception {byte buf[] new byte[1024];Datagr…

iOS 6 自動布局入門

http://www.raywenderlich.com/zh-hans/22873/ios-6-自動布局-入門&#xff0d;1轉載于:https://www.cnblogs.com/ihojin/p/auto-layout.html

Java GUI 基礎知識

這部分主要包含AWT、組件和容器、布局管理器Component&#xff1a;所有可以和用戶交互的圖形元素&#xff0c;他的子類有&#xff1a;輸入框… Java.awt及其子包 Container&#xff1a;容器&#xff0c;容納其他各種各樣的Component的元素。 Panel&#xff1a;可以容納其他元素…

UVA11300

初步解題原理:代數運算單元素極值 代數運算: xi表示第i個給i-1的數量&#xff0c;正負表示給或得 c(a1a2a3....an)/n a1-x1x2c -->x2x1-a1c a2-x2x3c -->x3x1-a1-a22c a3-x3x4c -->x4x1-a1-a2-a33c ...... an-xnx1c -->xnx1-a1-a2-a3....-a(n-1)(n-1)c ansmax{|x1|…

Java GUI 基礎知識2 監聽機制

TestActionEvent.java沒有調用方法&#xff0c;但是有反應。反應自己要編寫程序有反應。 事件模型&#xff1a;一定要有某些反應。 寫程序&#xff0c;監聽的操作是自動發生的&#xff0c;一直監聽。鉤子函數&#xff0c;&#xff08;回調函數&#xff09; 怎么讓它自動執行&am…

求字符串的最長回文字串 O(n)

昨天參加了某公司的校園招聘的筆試題&#xff0c;做得慘不忍睹&#xff0c;其中就有這么一道算法設計題&#xff1a;求一個字符串的最長回文字串。我在ACM校隊選拔賽上遇到過這道題&#xff0c;當時用的后綴數組AC的&#xff0c;但是模板忘了沒寫出代碼來。 回頭我把這道題目再…

數據結構 二、向量(接口與實現and可擴容向量)

ADT操作實例&#xff1a;Disordered&#xff1a;顯示出3對逆序緊鄰對。Vector模板類初始有效空間為0&#xff1b;基于復制的構造描述區間&#xff1a;左閉右開 為什么*2&#xff1f;有限時間內不必要為擴容而打斷。 2、可擴充向量左移一位&#xff1a;加一倍

數據庫:mysql 獲取剛插入行id[轉]

我們在寫數據庫程序的時候,經常會需要獲取某個表中的最大序號數, 一般情況下獲取剛插入的數據的id&#xff0c;使用select max(id) from table 是可以的。但在多線程情況下&#xff0c;就不行了。 下面介紹三種方法 (1) getGeneratedKeys()方法: 程序片斷: Connection conn ; …

svn由于連接方在一段時間后沒有正確答復或連接的主機沒有反應連接嘗試失敗...

解決方法&#xff0c;關掉防火墻&#xff0c; service iptables status 查看iptables狀態 service iptables restart iptables服務重啟 service iptables stop iptables服務禁用 轉載于:https://www.cnblogs.com/jiqing9006/p/3347441.html