本文主要記錄了一些本人學習多線程的一下筆記,可作爲多線程入門參考。
一、線程簡介
介紹線程之前要介紹程序和進程:
程序:程序是一個指令和數據的有效集合,其本身沒有任何允許的含義,是一個靜態的概念;
進程:是執行程序的一次執行過程,他是一個動態的概念,是系統資源分配的單元;
線程:是cup調度和執行的單位。通常在一個進程中可以包含若干個線程,當然一個進程中至少有一個線程,不然沒有存在的意義;
多線程:一個進程中的多個線程。
注意:很多多線程是模擬出來的,真正的多線程是隻有多個cpu,即多核,如服務器。如果是模擬出來的多線程,即使在一個cpu的情況下,在同一時間點,cpu只能執行一個代碼,因爲切換很快,所以就有同時執行的錯覺。
二、多線程的創建方式
1、繼承Thread類
//創建線程方式一:繼承Thread類,重寫run()方法,調用start開啓線程
//總結:注意,線程開啓不一定立即執行,有cpu調度執行
public class TestThread1 extends Thread{
@Override
public void run() {
//run方式線程實現體
for (int i = 0; i < 200; i++) {
System.out.println("我在看代碼---"+i);
}
}
public static void main(String[] args){
TestThread1 testThread1=new TestThread1();
//testThread1.run(); //調用run方法,立即執行子線程
testThread1.start();//調用start方法,開啓新的線程,但不一定立即執行,由cpu調度執行
//main線程,主線程
for (int i = 0; i < 200; i++) {
System.out.println("我在學習多線程---"+i);
}
}
}
執行結果如下:
可以看出來兩個線程是交替執行的,每次執行結果都不一樣,是由cpu的調度執行的。
2、實現Runnable接口
//創建方式線程方式2:實現runnable接口,重寫run方法,執行線程需要丟入runnable接口實現類,調用start方法
public class TestThread2 implements Runnable{
@Override
public void run() {
//run方式線程實現體
for (int i = 0; i < 200; i++) {
System.out.println("我在看代碼---"+i);
}
}
public static void main(String[] args){
//創建runnable接口的實現類對象
TestThread2 testThread2=new TestThread2();
//創建線程對象,通過線程對象來開啓我們的線程
Thread thread=new Thread(testThread2);
thread.start();//調用start方法,開啓新的線程,但不一定立即執行,由cpu調度執行
//main線程,主線程
for (int i = 0; i < 200; i++) {
System.out.println("我在學習多線程---"+i);
}
}
}
運行結果如下:
使用龜兔賽跑的案例來鞏固一下實現Runnable接口:
/**
* 模擬龜兔賽跑
* 1、首先來個賽道距離,然後要距離終點越來越近
* 2、判斷比賽是否結束
* 3、打印出勝利者
* 4、龜兔賽跑開始
* 5、故事中烏龜是贏得,兔子需要睡覺,所以我們來模擬兔子睡覺
* 6、烏龜贏得比賽
*
*/
public class Race implements Runnable{
//勝利者
private static String winner;
@Override
public void run() {
//定義跑道
for (int i = 0; i <= 100; i++) {
if(Thread.currentThread().getName().equals("兔子") && i%10==0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//判斷比賽是否結束
if(gamOver(i)){
break;
}
System.out.println(Thread.currentThread().getName()+"-->"+"跑了"+i+"步");
}
}
//判斷是否完成比賽
public boolean gamOver(int steps){
if(winner!=null){
return true;
}
if(steps>=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();
}
}
運行結果如下:
3、實現Callable接口
import java.io.File;
import java.net.URL;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.commons.io.FileUtils;
/**
* 創建線程方式三:實現callable接口
* 好處:可以定義返回值
* 可以拋出異常
*
*/
public class TestCallable implements Callable<Boolean>{
private String url;//圖片url
private String name;//保存的文件名
public TestCallable(String url,String name) {
this.url=url;
this.name=name;
}
@Override
public Boolean call() throws Exception {
WebDownloader webDownloader=new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下載了文件名爲:"+name);
return true;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
TestCallable t1=new TestCallable("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1209/26/c0/14139494_1348624365103.jpg","1.jpg");
TestCallable t2=new TestCallable("https://www.2008php.com/09_Website_appreciate/10-07-26/12801466092ttaRB6xsN.jpg","2.jpg");
TestCallable t3=new TestCallable("http://img.pconline.com.cn/images/upload/upc/tx/wallpaper/1308/17/c6/24564406_1376704633089.jpg","3.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();
System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);
//關閉服務
ser.shutdown();
}
}
class WebDownloader{
//下載方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (Exception e) {
e.printStackTrace();
System.out.println("io異常,下載方法出現問題5");
}
}
}
4、小結
三種方法的區別:
繼承Thread類:
1. 子類繼承Thread類具備多線程能力;
2. 啓動線程:子類對象.start();
不建議使用:避免OOP單繼承侷限性
實現Runnable接口
1.實現Runnable具有多線程能力;
2.啓動線程:傳入目標對象+Thread對象.start()
推薦使用:避免單繼承侷限性,靈活方便,方便同一個對象被多個線程使用
實現Callable接口
1.實現Collable接口,需要返回值類型;
2.重寫call方法,需要拋出異常
3.創建目標對象
4.創建執行服務
5.提交執行:Future<Boolean> result1=ser.submit(t1);
6.獲取結果:boolean r1=result1.get();
7.關閉服務:server,shutdownNow();
二、線程狀態
請看:https://blog.csdn.net/qq_33157666/article/details/103949045