Java-併發入門

来源:http://www.cnblogs.com/coffeeSS/archive/2016/05/28/5483995.html
-Advertisement-
Play Games

本文由@呆代待殆原創,轉載請註明出處:http://www.cnblogs.com/coffeeSS/ Java中實現多線程的方法 實現Runnable介面 實現Runnable介面里的run()方法,並將這個實例提交給一個Thread構造器,最後調用Thread.start()就可以啟動一個線程。 ...


 本文由@呆代待殆原創,轉載請註明出處:http://www.cnblogs.com/coffeeSS/

 

Java中實現多線程的方法

實現Runnable介面

實現Runnable介面里的run()方法,並將這個實例提交給一個Thread構造器,最後調用Thread.start()就可以啟動一個線程。

 1 public class MyRunnable implements Runnable {
 2     String name;
 3     public MyRunnable(String name){
 4         this.name=name;
 5     }
 6     @Override
 7     public void run() {
 8         for(int i=0;i<5;++i){
 9             System.out.println(name+" 第 "+i+" 次運行");
10             try {
11                 Thread.sleep(1000);
12             } catch (InterruptedException e) {
13                 e.printStackTrace();
14             }
15         }
16         System.out.println(name+" 運行完畢停機");
17     }
18 
19 }
View Code
1 public class Test {
2     public static void main(String[] args){
3         new Thread(new MyRunnable("0號機")).start();
4         new Thread(new MyRunnable("1號機")).start();
5     }
6 }
View Code

 

繼承Thread

直接繼承Thread類,重寫run()方法,調用Thread.start()即可

 1 public class MyThread extends Thread {
 2     public static void main(String[] args) {
 3         MyThread zero=new MyThread("0號機");
 4         MyThread one =new MyThread("1號機");
 5         zero.start();
 6         one.start();
 7     }
 8     String name;
 9     public MyThread(String name){
10         this.name=name;
11     }
12     public void run(){
13         for(int i=0;i<5;++i){
14             System.out.println(name+" 第 "+i+" 次運行");
15             try {
16                 Thread.sleep(1000);
17             } catch (InterruptedException e) {
18                 e.printStackTrace();
19             }
20         }
21         System.out.println(name+" 運行完畢停機");
22     }
23 }
View Code

 

線程的狀態

線程有五個狀態。

1、初始狀態

  Thread aThread = new Thread();

2、就緒狀態

  Thread aThread = new Thread();

  Thread.start();

3、阻塞狀態

  suspend()

  sleep()

  wait()

  輸入輸出流發生阻塞。

  線程同步時試圖鎖住另一個線程鎖住的對象。

  ...

4、運行狀態

  run()方法正在執行中。

5、死亡狀態

  stop(),或非預期的異常終止run()方法,線程突然死亡。

  run()正常退出,線程自然死亡。

 

線程相關的常用方法簡介(都有相應的線上API鏈接)

start()//啟動線程,調用run()方法。

run()//一般我們需要重寫這個方法給線程安排要執行的任務。

interrupt()//中斷這個線程。

yield()//會讓調用這個方法的線程睡眠。

wait()//讓調用此方法的線程阻塞。

wait與sleep是不同的,調用了sleep 的線程仍然會占用cpu,且不會釋放已經拿到的鎖,但是不會有任何動作,調用了wait的線程不會占用cpu,會讓出已經占用的鎖。

sleep(long millis)//讓當前線程睡眠millis毫秒

sleep(long millis, int nanos)//讓當前線程睡眠millis毫秒nanos納秒。

notify()//隨機喚醒一個阻塞狀態的線程。

notifyAll()//喚醒所有阻塞狀態的線程。

setPriority(int newPriority)//用來設置線程的優先順序JDK提供了10個優先順序所以newPriority的取值是1-10,其中10是最高優先順序。

註意大多數操作系統並不能和這10個優先順序很好的映射,比如window只有7個優先順序,而Sun的Solaris有231個優先順序,所以要想使優先順序的設置操作是可移植的,最好用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY這三個預定義的常數來設置優先順序。

getPriority()//返回線程優先順序。

toString()//返回線程的名字、優先順序和線程組。

setName(String name)//設置線程的名稱。

getName()//獲得線程的名字。

getId()//獲得線程的identifier

setDaemon(boolean on)//標識是否是一個守護線程。

join()//當前線程會被掛起,等待調用這個方法的目標線程執行結束後再被喚醒(如:你在某個線程里寫t.join(),則當前線程會掛起,t線程開始運行,t線程返回後,當前線程繼續執行)。

join(long millis)//作用同上,但是如果millis毫秒後目標線程還沒返回的話,目標線程會被強制中斷,然後返回當前線程。

join(long millis, int nanos)//同上,只是時間更精確了。

isAlive()//測試這個線程是否還活著。

interrupted()//測試這個線程是否被中斷。

 

關於線程的同步

synchronized關鍵字

synchronized有兩種用法,用來修飾方法和用來修飾代碼塊。

1,修飾方法

synchronized關鍵字修飾方法的時候,這個方法只能被一個線程調用,當第二個線程想同時訪問的時候,它將被阻塞直到第一個線程從方法返回。

如果一個對象中有很多個方法都被synchronized關鍵字修飾,由於它們是共用同一把鎖的,也就是說,對其中某一個方法進行調用,將使得所有synchronized方法都不能被這個線程以為的其他線程調用。

 

這裡要註意類的每一個實例都有一個自己的對象鎖,一個實例裡面的synchronized方法被訪問會導致這個實例內其他synchronized方法不能訪問,但是不會導致這個類的其他實例的synchronized方法不能用。

2,修飾代碼塊

修飾代碼塊的時候synchronized可以指定需要獲取的鎖,而且可以讓同步的代碼塊更加精確。

 

關於鎖,每個類有一個類鎖,每個類的實例有一個對象鎖,這兩把鎖互相不幹擾。

當synchronized修飾static方法或者修飾的代碼塊指定的鎖為XXX.class的話,將獲得類鎖,類鎖管理所有的static方法的訪問,獲取類鎖後,其他類的實例將無法訪問所有的static方法,但是非static方法即使是用synchronized修飾的也可以訪問。同理,即使獲得對象鎖後將使得其他非static的synchronized修飾的方法無法訪問,但是static方法可以被訪問,當然同一個對象可以同時獲得者兩把鎖。

 

另外類鎖只是一個抽象概念,並不存在真正的類鎖,這裡的類鎖是代表著這個類的對象的對象鎖。

 

使用Lock對象進行併發控制。

使用Lock對象的時候要註意有良好的格式,如下(圖片來自網路)

lock.lock()方法將會讓線程獲得鎖,如果這個時候有別的線程來取鎖,則會陷入阻塞狀態。

lock.unlock()方法會釋放鎖,寫在finally里的原因是為了讓任何情況下,鎖都能得到釋放,這裡要強調的是return語句要寫在try裡面,以確保unlock()不會過早的發生。

使用Lock與使用synchronized的區別在於,如果使用synchronized,如果線程沒有得到鎖,它將一直等待下去,如果使用Lock,通過調用tryLock方法可以實現在失敗的時候中斷等待去乾別的事情。詳細情況如下。

 

tryLock()//如果獲取了鎖立即返回true,如果別的線程正持有鎖,立即返回false。

tryLock(long timeout, TimeUnit unit)//如果獲取了鎖定立即返回true,如果別的線程正持有鎖,會等待參數給定的時間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時,返回false。

 

效率上,在資源競爭不是很激烈的情況下,synchronized的性能要更好,否則synchronized的性能會下降的很快,而Lock的性能一直很穩定。

 

死鎖簡介

當以下4個條件同時滿足的時候就會發生死鎖

1,互斥條件:存在不能共用的資源。

2,不可搶占條件:資源申請者不能強行的從資源占有者手中奪取資源,資源只能由占有者自願釋放。

3,請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。

4,環路等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。

對於java線程的鎖機制來說,互斥條件和不可搶占條件先天滿足,所以我們能做到的就是不要讓條件3和條件4也都滿足就可以了。具體細節這裡不做討論。

 

管道流用於線程間通信

管道流的應用非常簡單,相關的類有四個

用於位元組流的PipedInputStream與PipedOutputStream

用於字元流的PipedReader與PipedWriter

 

它們都是配套使用的,用於兩個線程間的通信,並且通信是單向的。

使用的基本流程如下,

1,創建對應的輸出流和輸入流。

2,用connect方法將它們連接起來,然後就可以使用了。

3,調用close方法釋放資源。

輸入流的read方法在沒有更多數據的時候會自動阻塞。

另外用管道流進行讀寫的時候必須保證相對應的兩個線程都不能退出,也不能阻塞。否則就會報java.io.IOException: Write/Read end dead 的錯誤。

下麵給出一個實例,用PipedReader與PipedWriter實現兩個線程間的通信,程式會不停的列印一段文字。

 1 public class TestPiped {
 2     PipedWriter pWriter;
 3     PipedReader pReader;
 4 
 5     public TestPiped() {
 6         pWriter = new PipedWriter();
 7         try {
 8             pReader = new PipedReader(pWriter);
 9         } catch (IOException e) {
10             e.printStackTrace();
11         }
12         new Thread(new MyReader(pReader)).start();
13         new Thread(new MyWriter(pWriter)).start();
14     }
15 
16     public static void main(String[] args) {
17         new TestPiped();
18     }
19 }
20 
21 class MyReader implements Runnable {
22     PipedReader pReader;
23 
24     public MyReader(PipedReader pReader) {
25         this.pReader = pReader;
26     }
27 
28     @Override
29     public void run() {
30         while (true) {
31             char c;
32             int i;
33             while (true) {
34                 try {
35                     i = pReader.read();
36                     if (i == -1)
37                         break;
38                     c = (char) i;
39                     System.out.print(c);
40                 } catch (IOException e) {
41                     e.printStackTrace();
42                 }
43             }
44             System.out.println();
45             notifyAll();
46         }
47     }
48 }
49 
50 class MyWriter implements Runnable {
51     PipedWriter pWriter;
52 
53     public MyWriter(PipedWriter pWriter) {
54         this.pWriter = pWriter;
55     }
56 
57     @Override
58     public void run() {
59         while (true) {
60             try {
61                 pWriter.write(new String("番茄,番茄,這裡是西紅柿,收到請回答\n"));
62                 Thread.sleep(2000);
63             } catch (IOException e) {
64                 e.printStackTrace();
65             } catch (InterruptedException e) {
66                 e.printStackTrace();
67             }
68         }
69     }
70 
71 }
View Code

 

 

參考資料:

1,《java編程思想》中文 第四版

2,線上java API http://tool.oschina.net/apidocs/apidoc?api=jdk_7u4


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Session是一個域 作用範圍:當前會話範圍 生命周期:當程式第一次調用request.getSession()創建出客戶端的session對象,30分鐘沒有操作認為超時,這個可以在web.xml中進行配置 調用session.nvalidate()方法銷毀session 伺服器非正常關閉時銷毀s ...
  • 首先參考 http://jingyan.baidu.com/article/456c463b99f4370a583144a8.html 創建一個項目,照著上面教程到最後一步的時候,系統自動生成entity類,dao類,sessionfactory類和對應的配置文件 在下麵新建一個Test1.java ...
  • 在經過了一段時間的開發後,我對Laravel框架的認識又在逐步的加深,對於這個世界占有量NO.1的框架... 我想說,我已經在逐步的感受到他的恐怖之處... 一.建表--Laravel在資料庫建表上有自己獨立內置的結構,可以完全不用藉助原生SQL語句或者SQLyogEnt、Navicat 這樣的建表 ...
  • 使用Eclipse上傳/下載Git項目 前提: + Eclipse已安裝EGit插件 + 已擁有GitLab/GitHub賬號 SSH方式 配置SSH授權 1. 本地生成授權Key Eclipse,選擇菜單: 2. 遠程托管配置本地授權的Public Key 進入GitLab管理首頁, 在 中輸入剛 ...
  • 表達式是PHP中一個重要的概念,可以把表達式理解為“任何有值的東西”。在本教程中涉及到表達式的語法,我們以“expr”來表示表達式。 下麵就是一個表達式: 在上面的例子中,當$x的值大於$y時,該表達式值為TRUE,否則為FALSE。 我們經常通過判斷一個表達式的值(包括具體數值和布爾值)來確定我們 ...
  • 先看列表是如何創建的: 同樣有兩種創建方式,但一般用第一種。 列表和元祖最大的不同就是列表是可以修改的。 老規矩,使用 help(list) ,真的是 help() 大法好呀。 好,來人,上代碼。 Help on class list in module __builtin__: class lis ...
  • 本文內容全部出自《Python基礎教程》第二版,在此分享自己的學習之路。 lxx___歡迎轉載:http://www.cnblogs.com/Marlowes/p/5537223.htmllxx___ Created on Marlowes 使用簡單的純文本文件只能實現有限的功能。沒錯,使用它們可以 ...
  • 1.連接MySQL資料庫 1.1.JDBC JDBC(Java Database Connectivity)提供了訪問資料庫的API,由一些Java類和介面組成,是Java運行平臺核心庫中的一部分。用Java語言編寫的資料庫驅動程式稱為JDBC數據驅動程式。 也就是說使用JDBC可以達到通過JDBC ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...