多執行緒學習小計
阿新 • • 發佈:2020-12-11
多執行緒複習學習
1. Thread
- 繼承Thread類,重寫run()方法,呼叫start開啟執行緒。
package com;
public class demo02 extends Thread{
@Override
public void run() {
//run方法執行緒體
for (int i = 0; i < 20; i++) {
System.out.println("run方法執行緒體:"+i);
}
}
public static void main(String[] args) {
//建立執行緒物件
demo02 test = new demo02();
//啟動執行緒
test.start();
for (int i = 0; i < 20; i++) {
System.out.println("主執行緒:"+i);
}
}
}
- 輸出結果如下,可見兩者交替執行,如果把start更改為run方法,則會先執行執行緒體方法再main方法;
- 注意,執行緒開始不一定立即執行,由cpu排程;
- 案例練習
package com;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//聯絡Thread,實現多執行緒同步下載圖片
public class demo3 extends Thread{
//網路圖片地址
private String url;
//儲存的檔名
private String name;
public demo3(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downloader(url,name);
System.out.println("下載了檔名為"+name);
}
public static void main(String[] args) {
demo3 t1 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg1.jpg");
demo3 t2 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg2.jpg");
demo3 t3 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg3.jpg");
t1.start();
t2.start();
t3.start();
}
}
class WebDownLoader{
//下載方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO異常,downloader方法出問題");
}
}
}
- 結果輸出如下,可見執行緒是同步進行的
2. Runnable
- 定義類實現runnable介面,實現run方法,編寫執行緒體,然後建立執行緒物件start啟動執行緒;
package com;
public class demo04 implements Runnable {
@Override
public void run() {
//run方法執行緒體
for (int i = 0; i < 20; i++) {
System.out.println("run方法執行緒體:"+i);
}
}
public static void main(String[] args) {
//建立類物件
demo02 test = new demo02();
//建立執行緒物件,對比繼承Thread執行緒的不同之處,代理
Thread thread = new Thread(test);
//啟動執行緒
thread.start();
for (int i = 0; i < 20; i++) {
System.out.println("主執行緒:"+i);
}
}
}
- 對比繼承Thread類的執行緒,實現Runnable介面的執行緒需要靜態代理,即Thread的物件做代理物件,將Runnable的物件做真實物件;
- 對案例進行修改,更改為實現Runnable介面的執行緒,如下:
//除了繼承Thread修改為實現Runnable介面外,只需更改以下程式碼
public static void main(String[] args) {
demo3 t1 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg1.jpg");
demo3 t2 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg2.jpg");
demo3 t3 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg3.jpg");
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}
3. 小結
1.繼承Thread類
- 子類繼承Thread類具備多執行緒能力;
- 啟動執行緒:子類物件.start();
- 不建議使用:避免OOP單繼承侷限性;
2.實現Runnable介面
- 實現介面Runnable具有多執行緒能力;
- 啟動執行緒:傳入目標物件+Thread物件.start();
- 推薦使用:避免單繼承侷限性,靈活方便,方便同一個物件被多個執行緒使用;
- 相較於繼承Thread,可以做到一份資源多個代理,因為繼承Thread的話啟動是子類物件.start,而實現Runnable介面的是傳入物件實現的,這樣就可以做到多個執行緒操作同一個物件,而不用新建多物件;
3.案例如下
- 以下案例確實實現了多執行緒操作同一物件,但會出現執行緒不安全的問題,即票拿重複了,這就需要做到執行緒同步才能解決,後續篇章講解。
package com;
public class demo06 implements Runnable{
//票數
private int ticketNums=10;
@Override
public void run() {
while (true){
if (ticketNums<=0){
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"張票");
}
}
public static void main(String[] args) {
demo06 ticket = new demo06();
new Thread(ticket,"小明").start();
new Thread(ticket,"老師").start();
new Thread(ticket,"小紅").start();
}
}
4. Callable
- 實現Callable介面,需要返回值型別;
- 重寫call方法,需要丟擲異常;
- 建立目標物件;
- 建立執行服務;
ExecutorService ser = Executors.newFixedThreadPool(3);//3個執行緒
- 提交執行;
Future<Boolean> r1 = ser.submit(t1);
- 獲取結果;
boolean rs1 = r1.get();
- 關閉服務;
ser.shutdownNow();
重寫下載圖片案例,實現Callable介面,如下:
package test;
import com.demo3;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class TestCallable implements Callable<Boolean> {
//網路圖片地址
private String url;
//儲存的檔名
private String name;
public TestCallable(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downloader(url,name);
System.out.println("下載了檔名為"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg1.jpg");
TestCallable t2 = new TestCallable("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg2.jpg");
TestCallable t3 = new TestCallable("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg3.jpg");
//建立執行服務
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交執行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//獲取結果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//關閉服務
ser.shutdownNow();
}
}
class WebDownLoader{
//下載方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO異常,downloader方法出問題");
}
}
}
- 相較於實現Runnable,Callable有如下好處:
- 可以定義返回值;
- 可以丟擲異常;