java多線程筆記part(Java多線程詳解)

本文目錄一覽:

java 多線程

所要執行的指令,也包括了執行指令所需的系統資源,不同進程所佔用的系統資源相對獨立。所以進程是重量級的任務,它們之間的通信和轉換都需要操作系統付出較大的開銷。

線程是進程中的一個實體,是被系統獨立調度和分派的基本單位。線程自己基本上不擁有系統資源,但它可以與同屬一個進程的其他線程共享進程所擁有的全部資源。所以線程是輕量級的任務,它們之間的通信和轉換隻需要較小的系統開銷。

Java支持多線程編程,因此用Java編寫的應用程序可以同時執行多個任務。Java的多線程機制使用起來非常方便,用戶只需關注程序細節的實現,而不用擔心後台的多任務系統。

Java語言里,線程表現為線程類。Thread線程類封裝了所有需要的線程操作控制。在設計程序時,必須很清晰地區分開線程對象和運行線程,可以將線程對象看作是運行線程的控制面板。在線程對象里有很多方法來控制一個線程是否運行,睡眠,掛起或停止。線程類是控制線程行為的唯一的手段。一旦一個Java程序啟動後,就已經有一個線程在運行。可通過調用Thread.currentThread方法來查看當前運行的是哪一個線程。

class ThreadTest{

public static void main(String args[]){

Thread t = Thread.currentThread();

t.setName(“單線程”); //對線程取名為”單線程”

t.setPriority(8);

//設置線程優先順序為8,最高為10,最低為1,默認為5

System.out.println(“The running thread: ” + t);

// 顯示線程信息

try{

for(int i=0;i3;i++){

System.out.println(“Sleep time ” + i);

Thread.sleep(100); // 睡眠100毫秒

}

}catch(InterruptedException e){// 捕獲異常

System.out.println(“thread has wrong”);

}

}

}

多線程的實現方法

繼承Thread類

可通過繼承Thread類並重寫其中的run()方法來定義線程體以實現線程的具體行為,然後創建該子類的對象以創建線程。

在繼承Thread類的子類ThreadSubclassName中重寫run()方法來定義線程體的一般格式為:

public class ThreadSubclassName extends Thread{

public ThreadSubclassName(){

….. // 編寫子類的構造方法,可預設

}

public void run(){

….. // 編寫自己的線程代碼

}

}

用定義的線程子類ThreadSubclassName創建線程對象的一般格式為:

ThreadSubclassName ThreadObject =

new ThreadSubclassName();

然後,就可啟動該線程對象表示的線程:

ThreadObject.start(); //啟動線程

應用繼承類Thread的方法實現多線程的程序。本程序創建了三個單獨的線程,它們分別列印自己的「Hello World!」。

class ThreadDemo extends Thread{

private String whoami;

private int delay;

public ThreadDemo(String s,int d){

whoami=s;

delay=d;

}

public void run(){

try{

sleep(delay);

}catch(InterruptedException e)

System.out.println(“Hello World!” + whoami

+ ” ” + delay);

}

}

public class MultiThread{

public static void main(String args[]){

ThreadDemo t1,t2,t3;

t1 = new ThreadDemo(“Thread1”,

(int)(Math.random()*2000));

t2 = new ThreadDemo(“Thread2”,

(int)(Math.random()*2000));

t3 = new ThreadDemo(“Thread3”,

(int)(Math.random()*2000));

t1.start();

t2.start();

t3.start();

}

}

實現Runnable介面

編寫多線程程序的另一種的方法是實現Runnable介面。在一個類中實現Runnable介面(以後稱實現Runnable介面的類為Runnable類),並在該類中定義run()方法,然後用帶有Runnable參數的Thread類構造方法創建線程。

創建線程對象可用下面的兩個步驟來完成:

(1)生成Runnable類ClassName的對象

ClassName RunnableObject = new ClassName();

(2)用帶有Runnable參數的Thread類構造方法創建線程對象。新創建的線程的指針將指向Runnable類的實例。用該Runnable類的實例為線程提供 run()方法—線程體。

Thread ThreadObject = new Thread(RunnableObject);

然後,就可啟動線程對象ThreadObject表示的線程:

ThreadObject.start();

在Thread類中帶有Runnable介面的構造方法有:

public Thread(Runnable target);

public Thread(Runnable target, String name);

public Thread(String name);

public Thread(ThreadGroup group,Runnable target);

public Thread(ThreadGroup group,Runnable target,

String name);

其中,參數Runnable target表示該線程執行時運行target的run()方法,String name以指定名字構造線程,ThreadGroup group表示創建線程組。

用Runnable介面實現的多線程。

class TwoThread implements Runnable{

TwoThread(){

Thread t1 = Thread.currentThread();

t1.setName(“第一主線程”);

System.out.println(“正在運行的線程: ” + t1);

Thread t2 = new Thread(this,”第二線程”);

System.out.println(“創建第二線程”);

t2.start();

try{

System.out.println(“第一線程休眠”);

Thread.sleep(3000);

}catch(InterruptedException e){

System.out.println(“第一線程有錯”);

}

System.out.println(“第一線程退出”);

}

public void run(){

try{

for(int i = 0;i 5;i++){

System.out.println(「第二線程的休眠時間:」

+ i);

Thread.sleep(1000);

}

}catch(InterruptedException e){

System.out.println(“線程有錯”);

}

System.out.println(“第二線程退出”);

}

public static void main(String args[]){

new TwoThread();

}

}

程序運行結果如下:

正在運行的線程: Thread[第一主線程,5,main

創建第二線程

第一線程休眠

第二線程的休眠時間:0

第二線程的休眠時間:1

第二線程的休眠時間:2

第一線程退出

第二線程的休眠時間:3

第二線程的休眠時間:4

第二線程退出

另外,虛機團上產品團購,超級便宜

如何使用Java編寫多線程程序(1)

一、簡介1、什麼是線程要說線程,就必須先說說進程,進程就是程序的運行時的一個實例。線程呢可以看作單獨地佔有CPU時間來執行相應的代碼的。對早期的計算機(如DOS)而言,線程既是進程,進程既是進程,因為她是單線程的。當然一個程序可以是多線程的,多線程的各個線程看上去像是並行地獨自完成各自的工作,就像一台一台計算機上運行著多個處理機一樣。在多處理機計算機上實現多線程時,它們確實可以並行工作,而且採用適當的分時策略可以大大提高程序運行的效率。但是二者還是有較大的不同的,線程是共享地址空間的,也就是說多線程可以同時讀取相同的地址空間,並且利用這個空間進行交換數據。 2、為什麼要使用線程為什麼要使用多線程呢?學過《計算機體系結構》的人都知道。將順序執行程序和採用多線程並行執行程序相比,效率是可以大大地提高的。比如,有五個線程thread1, thread2, thread3, thread4, thread5,所耗的CPU時間分別為4,5,1,2,7。(假設CPU輪換周期為4個CPU時間,而且線程之間是彼此獨立的)順序執行需要花費1Array個CPU時間,而並行需要的時間肯定少於1Array個CPU時間,至於具體多少時間要看那些線程是可以同時執行的。這是在非常小規模的情況下,要是面對大規模的進程之間的交互的話,效率可以表現得更高。 3、java中是如何實現多線程的與其他語言不一樣的是,線程的觀念在java是語言中是重要的,根深蒂固的,因為在java語言中的線程系統是java語言自建的, java中有專門的支持多線程的API庫,所以你可以以最快的速度寫一個支持線程的程序。在使用java創建線程的時候,你可以生成一個Thread類或者他的子類對象,並給這個對象發送start()消息(程序可以向任何一個派生自 Runnable 介面的類對象發送 start() 消息的),這樣一來程序會一直執行,直到run返回為止,此時該線程就死掉了。在java語言中,線程有如下特點:§ 在一個程序中而言,主線程的執行位置就是main。而其他線程執行的位置,程序員是可以自定義的。值得注意的是對Applet也是一樣。§ 每個線程執行其代碼的方式都是一次順序執行的。§ 一個線程執行其代碼是與其他線程獨立開來的。如果諸線程之間又相互協作的話,就必須採用一定的交互機制。§ 前面已經說過,線程是共享地址空間的,如果控制不當,這裡很有可能出現死鎖。 各線程之間是相互獨立的,那麼本地變數對一個線程而言就是完全獨立,私有的。所以呢,線程執行時,每個線程都有各自的本地變數拷貝。對象變數(instance variable)在線程之間是可以共享的,這也就是為什麼在java中共享數據對象是如此的好用,但是java線程不能夠武斷地訪問對象變數:他們是需要訪問數據對象的許可權的。二、準備知識 在分析這個例子之前,然我們先看看關於線程的幾個概念,上鎖,信號量,和java所提供的API。 上鎖對於大多數的程序而言,他們都需要線程之間相互的通訊來完成整個線程的生命周期,二實現線程之間同步的最簡單的辦法就是上鎖。為了防止相互關聯的兩個線程之間錯誤地訪問共享資源,線程需要在訪問資源的時候上鎖和解鎖,對於鎖而言,有讀鎖,寫鎖和讀寫鎖等不同的同步策略。在java中,所有的對象都有鎖;線程只需要使用synchronized關鍵字就可以獲得鎖。在任一時刻對於給定的類的實例,方法或同步的代碼塊只能被一個線程執行。這是因為代碼在執行之前要求獲得對象的鎖。 信號量通常情況下,多個線程所訪問為數不多的資源,那怎麼控制呢?一個比較非常經典而起非常簡單的辦法就是採用信號量機制。信號量機制的含義就是定義一個信號量,也就是說能夠提供的連接數;當有一個線程佔用了一個連接時,信號量就減一;當一個線程是放了連接時,信號量就加一。

java 如何實現多線程

java中多線程的實現方式有兩種,一種是繼承java.lang.Thread類,另一種是實現java.lang.Runnable介面。下面是兩種方式的簡單代碼。繼承Thread類方式:import java.lang.Thread; //用集成Thread類方式實現多線程。 public class Test{ public static void main(String arg[]){ T t1=new T(); T t2=new T(); //更改新線程名稱 t1.setName(“t1”); t2.setName(“t2″); //啟動線程 t1.start(); t2.start(); } } class T extends Thread{ //重寫run()方法 public void run(){ System.out.println(this.getName()); } }輸出結果為:t1t2實現Runnable介面方式:在使用Runnable介面時需要建立一個Thread實例。因此,無論是通過Thread類還是Runnable介面建立線程,都必須建立Thread類或它的子類的實例。import java.lang.*; //用實現Runnable介面的方式實現多線程。 public class Test{ public static void main(String arg[]){ T t1=new T(); T t2=new T(); //一定要實例化Thread對象,將實現Runnable介面的對象作為參數傳入。 Thread th1=new Thread(t1,”t1″); Thread th2=new Thread(t2,”t2”); //啟動線程 th1.start(); th2.start(); } } class T implements Runnable{ //重寫run()方法 public void run(){ System.out.println(Thread.currentThread().getName()); } }輸出結果為:t1t2public void run()方法是JAVA中線程的執行體方法,所有線程的操作都是從run方法開始,有點類似於main()方法,即主線程。

java多核多線程

java可以實現多線程.

但是不能判斷某個線程再哪個CPU上執行.

java實現多線程有兩種方法。

一種是繼承Thread類

class MyThread extends Thread{

@override

public void run(){

//要執行的代碼

}

}

調用時,使用new MyThread().start()就可以了

另一種方法是實現Runnable介面

class MyThread implements Runnable{

@override

public void run(){

//要執行的代碼

}

}

調用方法:

new Thread(new MyThread()).start()

多線程回顧筆記總結

三種創建方式:

// 練習Thread,實現多線程同步下載圖片

public class TestThread1 extends Thread {

private String name; // 保存文件名

// 創建一個構造器

public TestThread1(String url, String name) {

this.url = url;

this.name = name;

}

public static void main(String[] args) {

TestThread1 testThread1 = new TestThread1(“;z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=4spn=0di=179850pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1462782358%2C2840615474os=332435882%2C2135675601simid=3516664974%2C458125993adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3B8-jyj_z%26e3BvgAzdH3FQQ%25E0%25l9%25AF%25EF%25Bl%25ba%25E0%25ln%25A0%25Em%25BE%25Bm%25Em%25lD%25l8%25Ec%25b9%25lA_z%26e3Bip4sgsm=5islist=querylist=”,”1.jpg”);

TestThread1 testThread2 = new TestThread1(“;z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=8spn=0di=18480pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1756505232%2C2129310927os=3725694615%2C3696011658simid=3576571828%2C474240706adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3F80qq_z%26e3Bv54AzdH3Fqqp57xtwg2AzdH3F8899l9m_rn_z%26e3Bip4sgsm=5islist=querylist=”,”2.jpg”);

TestThread1 testThread3 = new TestThread1(“;z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=25spn=0di=132220pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1226648351%2C4217836os=1652635041%2C3404961290simid=3325093720%2C338083432adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Bprbb_z%26e3BgjpAzdH3Fp57xtwg2p7rtwgAzdH3Fgwgfijg2p57xtwg2AzdH3Fda8lAzdH3F8d89AzdH3F8dnb0_z%26e3Bip4sgsm=5islist=querylist=”,”3.jpg”);

testThread1.start();

testThread2.start();

testThread3.start();

/* 結果,說明線程不是按代碼先後順序執行的,是同時執行的

下載了文件,名為:3.jpg

下載了文件,名為:1.jpg

下載了文件,名為:2.jpg

*/

}

// 下載圖片的線程執行體

@Override

public void run() {

WebDownLoader webDownLoader = new WebDownLoader();

webDownLoader.downLoader(url,name);

System.out.println(“下載了文件,名為:”+name);

}

}

// 下載器

class WebDownLoader {

// 下載方法

public void downLoader(String url,String name) {

try {

// copyURLToFile(),這個方法就是把網上的一個url變成一個文件

FileUtils.copyURLToFile(new URL(url),new File(name));

} catch (IOException e) {

e.printStackTrace();

System.out.println(“IO異常,downLoader方法出錯”);

}

}

}

// 創建線程方式2:實現Runnable介面,重寫run()方法,執行線程需要丟入Runnable介面實現類,調用start()方法

public class TestThread2 implements Runnable {

public static void main(String[] args) {

// 創建Runnable 介面實現類對象

TestThread2 testThread2 = new TestThread2();

// 創建線程對象,通過線程對象來開啟我們的線程,代理

Thread thread = new Thread(testThread2);

thread.start();

//new Thread(testThread2).start(); // 簡寫方法

for (int i = 0; i 2000; i++) {

System.out.println(“我們在學習多線程—“+i);

}

}

@Override

public void run() {

for (int i = 0; i 2000; i++) {

System.out.println(“我們在玩 遊戲 啦—“+i);

}

}

}

發現問題:多個線程操作同一個資源情況下,線程不安全,數據紊亂,出現重複數據

// 模擬龜兔賽跑

public class Race implements Runnable {

// 勝利者

private static String winner;

@Override

public void run() {

for (int i = 0; i = 100) {

winner = Thread.currentThread().getName();

System.out.println(“winner is ” + winner);

return true;

}

}

return false;

}

public static void main(String[] args) {

Race race = new Race();

new Thread(race, “兔子”).start();

new Thread(race, “烏龜”).start();

}

}

關閉服務:executorService.shutdownNow();

// 線程創建方式三:實現Callable介面

/*

* Callable的好處

* 1.可以定義返回值

* 2.可以拋出異常

**/

public class TestCallable implements Callable {

private String name; // 保存文件名

// 創建一個構造器

public TestCallable(String url, String name) {

this.url = url;

this.name = name;

}

public static void main(String[] args) throws ExecutionException, InterruptedException {

TestCallable testThread1 = new TestCallable(“;z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=4spn=0di=179850pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1462782358%2C2840615474os=332435882%2C2135675601simid=3516664974%2C458125993adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3B8-jyj_z%26e3BvgAzdH3FQQ%25E0%25l9%25AF%25EF%25Bl%25ba%25E0%25ln%25A0%25Em%25BE%25Bm%25Em%25lD%25l8%25Ec%25b9%25lA_z%26e3Bip4sgsm=5islist=querylist=”,”1.jpg”);

TestCallable testThread2 = new TestCallable(“;z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=8spn=0di=18480pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1756505232%2C2129310927os=3725694615%2C3696011658simid=3576571828%2C474240706adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3F80qq_z%26e3Bv54AzdH3Fqqp57xtwg2AzdH3F8899l9m_rn_z%26e3Bip4sgsm=5islist=querylist=”,”2.jpg”);

TestCallable testThread3 = new TestCallable(“;z=0ipn=falseword=qq%E5%A4%B4%E5%83%8Fhs=0pn=25spn=0di=132220pi=0rn=1tn=baiduimagedetailis=0%2C0ie=utf-8oe=utf-8cl=2lm=-1cs=1226648351%2C4217836os=1652635041%2C3404961290simid=3325093720%2C338083432adpicid=0lpn=0ln=30fr=alafm=sme=cg=headbdtype=0oriquery=qq%E5%A4%B4%E5%83%8Fobjurl=http%3A%2F%2F;fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Bprbb_z%26e3BgjpAzdH3Fp57xtwg2p7rtwgAzdH3Fgwgfijg2p57xtwg2AzdH3Fda8lAzdH3F8d89AzdH3F8dnb0_z%26e3Bip4sgsm=5islist=querylist=”,”3.jpg”);

// 創建執行服務

ExecutorService executorService = Executors.newFixedThreadPool(3);

// 提交執行

Future submit1 = executorService.submit(testThread1);

Future submit2 = executorService.submit(testThread2);

Future submit3 = executorService.submit(testThread3);

// 獲取結果

Boolean b1 = submit1.get();

Boolean b2 = submit2.get();

Boolean b3 = submit3.get();

// 關閉服務

executorService.shutdownNow();

System.out.println(b1);

System.out.println(b2);

System.out.println(b3);

/* 結果,說明線程不是按代碼先後順序執行的,是同時執行的

下載了文件,名為:3.jpg

下載了文件,名為:1.jpg

下載了文件,名為:2.jpg

*/

}

// 下載圖片的線程執行體

@Override

public Boolean call() {

WebDownLoader webDownLoader = new WebDownLoader();

webDownLoader.downLoader(url,name);

System.out.println(“下載了文件,名為:”+name);

return true;

}

}

// 下載器

class WebDownLoader {

// 下載方法

public void downLoader(String url,String name) {

try {

// copyURLToFile(),這個方法就是把網上的一個url變成一個文件

FileUtils.copyURLToFile(new URL(url),new File(name));

} catch (IOException e) {

e.printStackTrace();

System.out.println(“IO異常,downLoader方法出錯”);

}

}

}

靜態代理模式總結:

真實對象和代理對象都要實現同一個介面

代理對象要代理真實角色

代理模式的好處:

代理對象可以做很多真實對象做不了的事情

真實對象專註做自己的事情就可以了, 其他的事情交給代理公司來做

new Thread(()- System.out.println(“我愛你啊”)).start();

public abstract void run();

}

五大狀態:

// 測試Stop

// 1.建議線程正常停止,利用次數,不建議死循環

// 2.建議使用標誌位,設置一個標誌位

// 3.不要使用stop(),destroy()方法,已過時

public class StopTest implements Runnable {

// 1 設置一個標誌位

private boolean flag = true;

public static void main(String[] args) {

StopTest stopTest = new StopTest();

// 開啟線程

new Thread(stopTest).start();

for (int i = 0; i 1000; i++) {

System.out.println(“main Thread is running…”+i);

if (i == 900) {

// 調用stop方法切換標誌位停止線程

stopTest.stop();

System.out.println(“Thread is stopped…”);

}

}

}

// 設置一個公開的方法停止線程,轉換標誌位

public void stop() {

this.flag = false;

}

@Override

public void run() {

int i = 0;

while (flag) {

System.out.println(“Thread is running…”+ i++);

}

}

}

yield

jion

daemon

// 化妝的方法,互相持有對方的鎖,就是需要拿到對方的資源

private void makeup() throws InterruptedException {

if (choice == 0) {

synchronized (lipstick) { // 獲得口紅的鎖

System.out.println(this.girlName+”獲得口紅的鎖”);

Thread.sleep(1000);

synchronized (mirror) { // 一秒後想獲得鏡子的鎖

System.out.println(this.girlName+”獲得鏡子的鎖”);

}

}

} else {

synchronized (mirror) { // 獲得鏡子的鎖

System.out.println(this.girlName+”獲得鏡子的鎖”);

Thread.sleep(2000);

synchronized (lipstick) { // 一秒後想獲得口紅的鎖

System.out.println(this.girlName+”獲得口紅的鎖”);

}

}

}

}

這樣synchronized 塊包裹著,就會導致程序卡死,只要不包裹著,就可以正常運行,如下:

// 死鎖,多個線程互相抱著對方需要的資源,然後形成僵持

public class DeadLock {

public static void main(String[] args) {

Makeup bestGirl = new Makeup(0, “婕兒”);

Makeup betterGirl = new Makeup(1, “珂兒”);

bestGirl.start();

betterGirl.start();

}

}

// 口紅

class Lipstick {

}

// 鏡子

class Mirror {

}

// 化妝

class Makeup extends Thread {

// 需要的資源只有一份,用static來保證只有一份

static Mirror mirror = new Mirror();

static Lipstick lipstick = new Lipstick();

int choice; // 選擇

String girlName; // 使用化妝品的人

public Makeup(int choice, String girlName) {

this.choice = choice;

this.girlName = girlName;

}

@Override

public void run() {

// 化妝

try {

makeup();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

// 化妝的方法,互相持有對方的鎖,就是需要拿到對方的資源

private void makeup() throws InterruptedException {

if (choice == 0) {

synchronized (lipstick) { // 獲得口紅的鎖

System.out.println(this.girlName+”獲得口紅的鎖”);

Thread.sleep(1000);

}

synchronized (mirror) { // 一秒後想獲得鏡子的鎖

System.out.println(this.girlName+”獲得鏡子的鎖”);

}

} else {

synchronized (mirror) { // 獲得鏡子的鎖

System.out.println(this.girlName+”獲得鏡子的鎖”);

Thread.sleep(2000);

}

synchronized (lipstick) { // 一秒後想獲得口紅的鎖

System.out.println(this.girlName+”獲得口紅的鎖”);

}

}

}

}

上面列出了死鎖的四個條件,我們只要想辦法破其中任意一個,或多個條件就可以避免死鎖發生

1.這是一個線程同步問題,生產者和消費者共享同一個資源,並且生產者和消費者之間相互依賴,互為條件.

2.Java提供了幾個方法解決線程之間的通信問題

3.解決方式1

並發協作模型「生產者/消費者模式」 —管程法

生產者將生產好的數據放入緩衝區,消費者從緩衝區拿出數據

4.解決方式2

// 測試線程池

public class PoolTest {

public static void main(String[] args) {

// 創建服務,創建池子

// newFixedThreadPool,參數為:線程池池子大小

ExecutorService service = Executors.newFixedThreadPool(10);

service.execute(new MyThread());

service.execute(new MyThread());

service.execute(new MyThread());

service.execute(new MyThread());

// 2.關閉連接

service.shutdown();

}

}

class MyThread implements Runnable {

@Override

public void run() {

System.out.println(Thread.currentThread().getName());

}

}

在Java 中多線程的實現方法有哪些,如何使用

Java多線程的創建及啟動

Java中線程的創建常見有如三種基本形式

1.繼承Thread類,重寫該類的run()方法。

複製代碼

1 class MyThread extends Thread {

2  

3     private int i = 0;

4

5     @Override

6     public void run() {

7         for (i = 0; i 100; i++) {

8             System.out.println(Thread.currentThread().getName() + ” ” + i);

9         }

10     }

11 }

複製代碼

複製代碼

1 public class ThreadTest {

2

3     public static void main(String[] args) {

4         for (int i = 0; i 100; i++) {

5             System.out.println(Thread.currentThread().getName() + ” ” + i);

6             if (i == 30) {

7                 Thread myThread1 = new MyThread();     // 創建一個新的線程  myThread1  此線程進入新建狀態

8                 Thread myThread2 = new MyThread();     // 創建一個新的線程 myThread2 此線程進入新建狀態

9                 myThread1.start();                     // 調用start()方法使得線程進入就緒狀態

10                 myThread2.start();                     // 調用start()方法使得線程進入就緒狀態

11             }

12         }

13     }

14 }

複製代碼

如上所示,繼承Thread類,通過重寫run()方法定義了一個新的線程類MyThread,其中run()方法的方法體代表了線程需要完成的任務,稱之為線程執行體。當創建此線程類對象時一個新的線程得以創建,並進入到線程新建狀態。通過調用線程對象引用的start()方法,使得該線程進入到就緒狀態,此時此線程並不一定會馬上得以執行,這取決於CPU調度時機。

2.實現Runnable介面,並重寫該介面的run()方法,該run()方法同樣是線程執行體,創建Runnable實現類的實例,並以此實例作為Thread類的target來創建Thread對象,該Thread對象才是真正的線程對象。

複製代碼

1 class MyRunnable implements Runnable {

2     private int i = 0;

3

4     @Override

5     public void run() {

6         for (i = 0; i 100; i++) {

7             System.out.println(Thread.currentThread().getName() + ” ” + i);

8         }

9     }

10 }

複製代碼

複製代碼

1 public class ThreadTest {

2

3     public static void main(String[] args) {

4         for (int i = 0; i 100; i++) {

5             System.out.println(Thread.currentThread().getName() + ” ” + i);

6             if (i == 30) {

7                 Runnable myRunnable = new MyRunnable(); // 創建一個Runnable實現類的對象

8                 Thread thread1 = new Thread(myRunnable); // 將myRunnable作為Thread target創建新的線程

9                 Thread thread2 = new Thread(myRunnable);

10                 thread1.start(); // 調用start()方法使得線程進入就緒狀態

11                 thread2.start();

12             }

13         }

14     }

15 }

複製代碼

相信以上兩種創建新線程的方式大家都很熟悉了,那麼Thread和Runnable之間到底是什麼關係呢?我們首先來看一下下面這個例子。

複製代碼

1 public class ThreadTest {

2

3     public static void main(String[] args) {

4         for (int i = 0; i 100; i++) {

5             System.out.println(Thread.currentThread().getName() + ” ” + i);

6             if (i == 30) {

7                 Runnable myRunnable = new MyRunnable();

8                 Thread thread = new MyThread(myRunnable);

9                 thread.start();

10             }

11         }

12     }

13 }

14

15 class MyRunnable implements Runnable {

16     private int i = 0;

17

18     @Override

19     public void run() {

20         System.out.println(“in MyRunnable run”);

21         for (i = 0; i 100; i++) {

22             System.out.println(Thread.currentThread().getName() + ” ” + i);

23         }

24     }

25 }

26

27 class MyThread extends Thread {

28

29     private int i = 0;

30  

31     public MyThread(Runnable runnable){

32         super(runnable);

33     }

34

35     @Override

36     public void run() {

37         System.out.println(“in MyThread run”);

38         for (i = 0; i 100; i++) {

39             System.out.println(Thread.currentThread().getName() + ” ” + i);

40         }

41     }

42 }

複製代碼

同樣的,與實現Runnable介面創建線程方式相似,不同的地方在於

1 Thread thread = new MyThread(myRunnable);

那麼這種方式可以順利創建出一個新的線程么?答案是肯定的。至於此時的線程執行體到底是MyRunnable介面中的run()方法還是MyThread類中的run()方法呢?通過輸出我們知道線程執行體是MyThread類中的run()方法。其實原因很簡單,因為Thread類本身也是實現了Runnable介面,而run()方法最先是在Runnable介面中定義的方法。

1 public interface Runnable {

2  

3     public abstract void run();

4  

5 }

我們看一下Thread類中對Runnable介面中run()方法的實現:

複製代碼

@Override

public void run() {

if (target != null) {

target.run();

}

}

複製代碼

也就是說,當執行到Thread類中的run()方法時,會首先判斷target是否存在,存在則執行target中的run()方法,也就是實現了Runnable介面並重寫了run()方法的類中的run()方法。但是上述給到的列子中,由於多態的存在,根本就沒有執行到Thread類中的run()方法,而是直接先執行了運行時類型即MyThread類中的run()方法。

3.使用Callable和Future介面創建線程。具體是創建Callable介面的實現類,並實現clall()方法。並使用FutureTask類來包裝Callable實現類的對象,且以此FutureTask對象作為Thread對象的target來創建線程。

看著好像有點複雜,直接來看一個例子就清晰了。

複製代碼

1 public class ThreadTest {

2

3     public static void main(String[] args) {

4

5         CallableInteger myCallable = new MyCallable();    // 創建MyCallable對象

6         FutureTaskInteger ft = new FutureTaskInteger(myCallable); //使用FutureTask來包裝MyCallable對象

7

8         for (int i = 0; i 100; i++) {

9             System.out.println(Thread.currentThread().getName() + ” ” + i);

10             if (i == 30) {

11                 Thread thread = new Thread(ft);   //FutureTask對象作為Thread對象的target創建新的線程

12                 thread.start();                      //線程進入到就緒狀態

13             }

14         }

15

16         System.out.println(“主線程for循環執行完畢..”);

17      

18         try {

19             int sum = ft.get();            //取得新創建的新線程中的call()方法返回的結果

20             System.out.println(“sum = ” + sum);

21         } catch (InterruptedException e) {

22             e.printStackTrace();

23         } catch (ExecutionException e) {

24             e.printStackTrace();

25         }

26

27     }

28 }

29

30

31 class MyCallable implements CallableInteger {

32     private int i = 0;

33

34     // 與run()方法不同的是,call()方法具有返回值

35     @Override

36     public Integer call() {

37         int sum = 0;

38         for (; i 100; i++) {

39             System.out.println(Thread.currentThread().getName() + ” ” + i);

40             sum += i;

41         }

42         return sum;

43     }

44

45 }

複製代碼

首先,我們發現,在實現Callable介面中,此時不再是run()方法了,而是call()方法,此call()方法作為線程執行體,同時還具有返回值!在創建新的線程時,是通過FutureTask來包裝MyCallable對象,同時作為了Thread對象的target。那麼看下FutureTask類的定義:

1 public class FutureTaskV implements RunnableFutureV {

2  

3     //….

4  

5 }

1 public interface RunnableFutureV extends Runnable, FutureV {

2  

3     void run();

4  

5 }

於是,我們發現FutureTask類實際上是同時實現了Runnable和Future介面,由此才使得其具有Future和Runnable雙重特性。通過Runnable特性,可以作為Thread對象的target,而Future特性,使得其可以取得新創建線程中的call()方法的返回值。

執行下此程序,我們發現sum = 4950永遠都是最後輸出的。而「主線程for循環執行完畢..」則很可能是在子線程循環中間輸出。由CPU的線程調度機制,我們知道,「主線程for循環執行完畢..」的輸出時機是沒有任何問題的,那麼為什麼sum =4950會永遠最後輸出呢?

原因在於通過ft.get()方法獲取子線程call()方法的返回值時,當子線程此方法還未執行完畢,ft.get()方法會一直阻塞,直到call()方法執行完畢才能取到返回值。

上述主要講解了三種常見的線程創建方式,對於線程的啟動而言,都是調用線程對象的start()方法,需要特別注意的是:不能對同一線程對象兩次調用start()方法。

你好,本題已解答,如果滿意

請點右下角「採納答案」。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/254273.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-14 17:41
下一篇 2024-12-14 17:41

相關推薦

  • java client.getacsresponse 編譯報錯解決方法

    java client.getacsresponse 編譯報錯是Java編程過程中常見的錯誤,常見的原因是代碼的語法錯誤、類庫依賴問題和編譯環境的配置問題。下面將從多個方面進行分析…

    編程 2025-04-29
  • Java JsonPath 效率優化指南

    本篇文章將深入探討Java JsonPath的效率問題,並提供一些優化方案。 一、JsonPath 簡介 JsonPath是一個可用於從JSON數據中獲取信息的庫。它提供了一種DS…

    編程 2025-04-29
  • Java騰訊雲音視頻對接

    本文旨在從多個方面詳細闡述Java騰訊雲音視頻對接,提供完整的代碼示例。 一、騰訊雲音視頻介紹 騰訊雲音視頻服務(Cloud Tencent Real-Time Communica…

    編程 2025-04-29
  • Java Bean載入過程

    Java Bean載入過程涉及到類載入器、反射機制和Java虛擬機的執行過程。在本文中,將從這三個方面詳細闡述Java Bean載入的過程。 一、類載入器 類載入器是Java虛擬機…

    編程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介紹

    本文將詳細介紹Java Milvus SearchParam withoutFields的相關知識和用法。 一、什麼是Java Milvus SearchParam without…

    編程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java語言中的一個版本,於2014年3月18日發布。本文將從多個方面對Java 8中某一周的周一進行詳細的闡述。 一、數組處理 Java 8新特性之一是Stream…

    編程 2025-04-29
  • Java判斷字元串是否存在多個

    本文將從以下幾個方面詳細闡述如何使用Java判斷一個字元串中是否存在多個指定字元: 一、字元串遍歷 字元串是Java編程中非常重要的一種數據類型。要判斷字元串中是否存在多個指定字元…

    編程 2025-04-29
  • VSCode為什麼無法運行Java

    解答:VSCode無法運行Java是因為默認情況下,VSCode並沒有集成Java運行環境,需要手動添加Java運行環境或安裝相關插件才能實現Java代碼的編寫、調試和運行。 一、…

    編程 2025-04-29
  • Python多線程讀取數據

    本文將詳細介紹多線程讀取數據在Python中的實現方法以及相關知識點。 一、線程和多線程 線程是操作系統調度的最小單位。單線程程序只有一個線程,按照程序從上到下的順序逐行執行。而多…

    編程 2025-04-29
  • Java任務下發回滾系統的設計與實現

    本文將介紹一個Java任務下發回滾系統的設計與實現。該系統可以用於執行複雜的任務,包括可回滾的任務,及時恢復任務失敗前的狀態。系統使用Java語言進行開發,可以支持多種類型的任務。…

    編程 2025-04-29

發表回復

登錄後才能評論