1. 程式人生 > 其它 >JavaSE-多執行緒

JavaSE-多執行緒

JavaSE-多執行緒

程式、程序、執行緒

  • 程式(program)指的是指令和資料的有序集合,其本身沒有執行的含義,是一個靜態的概念。
  • 程序(process)指的是一個程式執行一次的過程,他是一個動態的概念,是系統分配資源的單位
  • 執行緒(thread),通常在一個程序中可以有多個執行緒,但是在程序中至少存在一個執行緒(主執行緒),是CPU排程和執行的單位

真正的多執行緒指的是有多個CPU,即多核。

在程式執行時,即便沒有自己建立執行緒也會有很多執行緒,如主執行緒、gc執行緒、

在一個程序中,如果開闢了多個執行緒,執行緒的執行由排程器(CPU)安排排程,排程器是與作業系統密切相關的,先後順序不能人為干預

對一份資源進行操作時,會存在資源搶奪問題,需要加入併發控制,對執行緒排隊

每個執行緒在自己的工作記憶體互動,記憶體控制不當會導致資料不一致

執行緒實現

  1. 繼承Thread
  2. 實現 Runable 介面

ps:因為Java是單繼承,建議使用Runable介面實現多執行緒。方便同一個物件被多個執行緒使用

TestThread testThread = new TestThread();

new Thread(testThread,"執行緒1").start()
new Thread(testThread,"執行緒2").start()
new Thread(testThread,"執行緒3").start()

extends Thread

  1. 自定義執行緒繼承Thread
  2. 重寫run()方法,編寫執行緒執行體
  3. 建立執行緒物件,呼叫start()方法啟動現場,但執行緒不一定立即執行,聽CPU安排排程
//建立執行緒1:繼承Thread類,重寫run()方法,呼叫start()方法執行執行緒
public class CreateThreadDemo01 extends Thread{

    //重寫run()方法

    @Override
    public void run() {
        //run執行緒,run()方法體
        System.out.println("Run()執行緒========" + i);
    }

    public static void main(String[] args) {

        //main執行緒,主執行緒
        //建立一個執行緒物件
        CreateThreadDemo01 createThreadDemo01 = new CreateThreadDemo01();

        //呼叫start()方法執行執行緒
        createThreadDemo01.start();

        for (int i = 0; i < 2000; i++) {
            System.out.println("Main執行緒========" + i);
        }

    }
}

其中,主執行緒和run()方法執行緒交替執行,並不存在先後順序。

執行緒開啟不一定立即執行,由CPU排程執行。

載入第三方工具類庫

新建一個lib包,將下載好的jar包cv到lib包中,並右鍵 Add as Library

在專案資源目錄Project StructureLibraries下就可以看到這個jar包了

繼承Thread類下載檔案

  1. 繼承Thread方法
  2. 建立一個類,在其中建立一個方法呼叫org.apache.commons.io.中的FileUtils.copyURLToFile()方法下載網路上的檔案
  3. 重寫run()方法呼叫上面寫好的下載檔案的方法
  4. 建立有參構造方法使新建thread時可以直接穿入引數urlfilename
  5. main()方法中新建子執行緒並start()子執行緒
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;

//練習Thread 實現多執行緒同步下載
public class TestThreadDemo02 extends Thread{

    private String url;	//網路檔案的url
    private String filename;	//要儲存的檔名

    @Override
    public void run() {
        //子執行緒要進行的操作
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url, filename);
        System.out.println("檔案下載完成,位於: " + filename);

    }
  
		//有參構造方法
    public TestThreadDemo02(String url, String filename){
        this.url = url;         
        this.filename = filename;       
    }

    public static void main(String[] args) {

      	//建立子執行緒
        TestThreadDemo02 thread01 = new TestThreadDemo02("https://pic.cnblogs.com/avatar/1835657/20210311181843.png",
                "/Users/b/Desktop/01.png");
        TestThreadDemo02 thread02 = new TestThreadDemo02("https://pic.cnblogs.com/avatar/1835657/20210311181843.png",
                "/Users/b/Desktop/02.png");
        TestThreadDemo02 thread03 = new TestThreadDemo02("https://images.cnblogs.com/cnblogs_com/ache/1644357/o_20022410503214.jpg",
                "/Users/b/Desktop/03.jpg");
				//執行子執行緒
        thread01.start();   
        thread02.start();
        thread03.start();

    }
}

class WebDownloader {
    public void downloader(String url, String filename){
        try {
            FileUtils.copyURLToFile(new URL(url), new File(filename));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO錯誤,WebDownloader方法存在問題");
        }
    }
}

implements Runable

  1. implements Runnable介面
  2. 重寫run()方法
  3. 需要一個實現Runable介面的類的物件作為引數傳入Thread類中呼叫start()方法
//實現runable介面,重寫run方法,需要一個runable介面的實現類作為引數傳入thread類中呼叫start方法 實現多執行緒
//實現runable介面
public class RunableThreadDemo01 implements Runnable{


    public static void main(String[] args) {
        //new一個runable介面的實現類
        RunableThreadDemo01 runableThreadDemo01 = new RunableThreadDemo01();

        //傳入實現類並呼叫start
        new Thread(runableThreadDemo01).start();

        for (int i = 0; i <2000; i++) {
            System.out.println("main執行緒========" + i);
        }

    }

    //重寫run方法
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("run執行緒===========" + i);
        }
    }
}

實現Runable介面多執行緒下載檔案

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;

//實現多執行緒
public class TestThreadDemo01 implements Runnable{

    private String url;
    private String filename;

    @Override
    public void run() {
        //子執行緒要進行的操作
        WebDownloader02 webDownloader02 = new WebDownloader02();
        webDownloader02.downloader(url, filename);
        System.out.println("檔案下載完成,位於: " + filename);

    }

    public TestThreadDemo01(String url, String filename){
        this.url = url;         //網路檔案的url
        this.filename = filename;       //檔名
    }

    public static void main(String[] args) {

        TestThreadDemo02 thread01 = new TestThreadDemo02("https://pic.cnblogs.com/avatar/1835657/20210311181843.png",
                "/Users/b/Desktop/01.png");
        TestThreadDemo02 thread02 = new TestThreadDemo02("https://pic.cnblogs.com/avatar/1835657/20210311181843.png",
                "/Users/b/Desktop/02.png");
        TestThreadDemo02 thread03 = new TestThreadDemo02("https://images.cnblogs.com/cnblogs_com/ache/1644357/o_20022410503214.jpg",
                "/Users/b/Desktop/03.jpg");

        new Thread(thread01).start();   //執行該子執行緒
        new Thread(thread02).start();
        new Thread(thread03).start();

    }
}

class WebDownloader02 {
    public void downloader(String url, String filename){
        try {
            FileUtils.copyURLToFile(new URL(url), new File(filename));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO錯誤,WebDownloader方法存在問題");
        }
    }
}

多個執行緒操作一個物件

public class TestThreadDemo03 implements Runnable{

    private int ticketNums = 10;

    @Override
    public void run() {
        while (true){
            if (ticketNums >= 0){
                try {
                    //模擬延時
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums + "票");
                ticketNums--;
            }else{
                break;
            }
        }
    }


    public static void main(String[] args) {

        TestThreadDemo03 testThreadDemo03 = new TestThreadDemo03();

        new Thread(testThreadDemo03, "Ago").start();
        new Thread(testThreadDemo03, "Kfei").start();
        new Thread(testThreadDemo03, "Xming").start();
        new Thread(testThreadDemo03, "Zgou").start();

    }
}

執行結果,其中Kfei和Ago都拿到了第2票,第10票,多個執行緒操作同一個資源時出現數據混亂的問題

Kfei拿到了第10票
Ago拿到了第10票
Kfei拿到了第9票
Ago拿到了第8票
Kfei拿到了第7票
Ago拿到了第6票
Kfei拿到了第5票
Ago拿到了第4票
Ago拿到了第2票
Kfei拿到了第2票
Ago拿到了第1票
Kfei拿到了第0票

執行緒狀態

執行緒五大狀態

  1. 建立狀態:當new一個新的執行緒物件時,就進入了建立狀態
  2. 就緒狀態:當呼叫start()方法時,執行緒進入就緒狀態,但不是立即執行,需要等待CPU排程
  3. 阻塞狀態:當呼叫sleep()wait()或同步鎖定時,執行緒進入阻塞狀態,等阻塞事件結束後重新進入就緒狀態
  4. 執行狀態:獲得CPU資源真正開始執行執行緒
  5. 死亡狀態:執行緒中斷或結束

執行緒方法

方法 說明
setPriority(int newPriority) 更改執行緒優先順序
static void sleep(long millis) 使當前執行緒休眠指定的毫秒數
void join() 等待該執行緒中止
static void yeild() 暫停當前執行的執行緒物件,並執行其他執行緒
void interupt() 中斷執行緒(不建議)
boolean isAlive() 判斷執行緒是否存活

停止執行緒

推薦設定迴圈次數使用標誌位來控制執行緒的啟停

//建立標誌位,
    private boolean flag = true;

    @Override
    public void run() {
        //利用標誌位控制執行緒啟停
        int i = 0;
        while (flag){
            System.out.println("執行緒" + Thread.currentThread().getName() + "開始 " + i++);
        }
    }

    //對外提供方法用於停止執行緒
    public void stop(){
        this.flag = false;
        System.out.println("執行緒" + Thread.currentThread().getName() + "結束");
    }
		public static void main(String[] args) {
        	TestStopDemo testStopDemo = new TestStopDemo();
       	  new Thread(testStopDemo, "Thread1").start();
       	  new Thread(testStopDemo, "Thread2").start();
        
         testStopDemo.stop();     

        }

執行緒休眠 sleep()

 @Override
    public void run() {
        while (true){
            if (ticketNums >= 0){
                try {
                    //模擬延時
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
            }
        }
    }

模擬倒計時

public class TestTimeDownDemo implements Runnable{
    @Override
    public void run() {
        Date starttime = new Date(System.currentTimeMillis());//獲取系統當前時間

        while (true){
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(starttime));
                starttime = new Date(System.currentTimeMillis());//更新當前時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) {
        TestTimeDownDemo testTimeDownDemo = new TestTimeDownDemo();
        new Thread(testTimeDownDemo).start();
    }
}

執行緒禮讓 yield()

禮讓不一定成功,看CPU

public class TestYieldDemo {

    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"thread1").start();
        new Thread(myYield,"thread2").start();
    }

}

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "執行緒開始執行");
        Thread.yield();     //執行緒禮讓
        System.out.println(Thread.currentThread().getName() + "執行緒停止執行");
    }
}

執行緒強制執行 join()

Join()合併執行緒,待此執行緒執行結束後,再執行其他執行緒,這期間其他執行緒阻塞

public class TestJoinDemo implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 20000; i++) {
            System.out.println("join執行緒執行" + i);
        }
    }

    public static void main(String[] args) {
        //執行子執行緒
        TestJoinDemo testJoinDemo = new TestJoinDemo();

        Thread thread = new Thread(testJoinDemo);
        thread.start();

        //main執行緒
        for (int i = 0; i < 200; i++) {
            if (i==20){
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main執行緒" + i);

        }
    }
}
所有內容僅限於維護網路安全學習參考