Java 多線程 線程安全 教程

多線程教程

1、引言

最近一直想複習一下Java基礎,今天主要回顧一下多線程的操作

2、多線程

學習多線程需要了解2個概念:進程、線程

標題 描述
進程 進程是代碼在數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位
線程 線程是進程的一個執行路徑,一個進程中至少有一個線程,進程中的多個線程共享進程的 資源

概述:一個程序就是一個進程,舉個例子一個賣電影票的javaweb系統,當他啓動之後它就是一個進程了,我們給這個進程一個名字“賣電影票進程”。
線程指的是存活在一個進程中的工作任務,一個進程至少要有一個線程,也可以有多個線程,這個就需要通過代碼來控制了,假設賣電影軟件中有5個窗口同時賣票,我們通過線程的方式寫了5個線程可以同時進行賣票,這5個窗口線程就是“1窗口線程”、“2窗口線程”、“3窗口線程”、“4窗口線程”、“5窗口線程”,他們5個線程共同爲“賣電影票進程”服務。

3、併發和並行

併發:指兩個或多個事件在同一個時間段內發生。
並行:指兩個或多個事件在同一時刻發生(同時發生)。

4、多線程的使用

4.1、創建線程方式一

Java使用 java.lang.Thread 類代表線程,所有的線程對象都必須是Thread類或其子類的實例。每個線程的作用是 完成一定的任務,實際上就是執行一段程序流即一段順序執行的代碼。Java使用線程執行體來代表這段程序流。 Java中通過繼承Thread類來創建並啓動多線程的步驟如下:

  1. 定義Thread類的子類,並重寫該類的run()方法,該run()方法的方法體就代表了線程需要完成的任務,因此把 run()方法稱爲線程執行體。
  2. 創建Thread子類的實例,即創建了線程對象 。
  3. 調用線程對象的start()方法來啓動該線程。
  4. 在這裏插入代碼片
//創建一個類,繼承Thread線程類
package com.study.thread;

public class MyThread extends Thread {

    //給線程起名字
    public MyThread(String name){
        super(name);
    }

    //重寫run方法,run方法代表線程的實際執行方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + "正在執行" + i + "號");
        }

    }
}

//Main方法中,創建線程對象,使用start方法調用線程運行。
package com.study;

import com.study.thread.MyThread;

public class Main {

    public static void main(String[] args) {
        MyThread myThread1 = new MyThread("線程1");
        MyThread myThread2 = new MyThread("線程2");
        myThread1.start();
        myThread2.start();
    }

}

4.2、創建線程方式二

採用 java.lang.Runnable 也是非常常見的一種,我們只需要重寫run方法即可。 步驟如下:

  1. 定義Runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體。
  2. 創建Runnable實現類的實例,並以此實例作爲Thread的target來創建Thread對象,該Thread對象纔是真正 的線程對象。
  3. 調用線程對象的start()方法來啓動線程。
package com.study.runable;

public class MyRunable implements Runnable {


    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }

    
}

package com.study.main;

import com.study.runable.MyRunable;

public class MainMyRunnable {

    public static void main(String[] args) {

        MyRunable myRunable = new MyRunable();
        Thread thread = new Thread(myRunable, "自定義線程");
        thread.start();
    }


}

4.3 線程安全

當多個線程同時操作同一資源時,會造成數據紊亂問題,具體表現如下,假設銷售電影票一共有1-10號十張電影票,多線程的正常操作應該是處理10次將10張票都銷售出去了,但是如果沒有同步代碼的支持,那麼會造成某一張或某幾張票被銷售了多次,這明顯是不正確的,爲了解決這個問題我們需要學習如下技術。

4.3.1 同步代碼快

package com.study.syn;

public class MySynThread implements Runnable {

    private int ticketNum = 100;

    Object lock = new Object();

    public void run() {
        while (true){
            synchronized (lock){
                if(ticketNum > 0){
                    System.out.println(Thread.currentThread().getName() + "---->正在銷售底" + ticketNum + "張票");
                    ticketNum--;
                }
            }
        }
    }

}

package com.study.main;

import com.study.syn.MySynThread;

public class MySynMain {

    public static void main(String[] args) {
        MySynThread runnable = new MySynThread();
        Thread thread1 = new Thread(runnable, "線程1");
        Thread thread2 = new Thread(runnable, "線程2");
        Thread thread3 = new Thread(runnable, "線程3");
        thread1.start();
        thread2.start();
        thread3.start();
    }

}

4.3.2 同步方法

package com.study.syn;

public class MySynThread implements Runnable {

    private int ticketNum = 100;

    Object lock = new Object();

    public void run() {
        while (true){
            synchronized (lock){
                saleFun();
            }
        }
    }


    public synchronized void saleFun(){
        if(ticketNum > 0){
            System.out.println(Thread.currentThread().getName() + "---->正在銷售底" + ticketNum + "張票");
            ticketNum--;
        }
    }

}

package com.study.main;

import com.study.syn.MySynThread;

public class MySynMain {

    public static void main(String[] args) {
        MySynThread runnable = new MySynThread();
        Thread thread1 = new Thread(runnable, "線程1");
        Thread thread2 = new Thread(runnable, "線程2");
        Thread thread3 = new Thread(runnable, "線程3");
        thread1.start();
        thread2.start();
        thread3.start();
    }

}

4.3.3 鎖機制

package com.study.main;

import com.study.syn.MySynThread;

public class MySynMain {

    public static void main(String[] args) {
        MySynThread runnable = new MySynThread();
        Thread thread1 = new Thread(runnable, "線程1");
        Thread thread2 = new Thread(runnable, "線程2");
        Thread thread3 = new Thread(runnable, "線程3");
        thread1.start();
        thread2.start();
        thread3.start();
    }

}

package com.study.syn;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MySynThread implements Runnable {

    private int ticketNum = 100;

    Lock lock = new ReentrantLock();

    public void run() {
        while (true){
            try {
                lock.lock();
                if(ticketNum > 0){
                    Thread.sleep(20);
                    System.out.println(Thread.currentThread().getName() + "---->正在銷售第" + ticketNum + "張票");
                    ticketNum--;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

4.4 線程的狀態

線程狀態 導致線程狀態發生條件
NEW(新建) 線程剛被創建,但是並未啓動。還沒調用start方法。
Runnable(可 運行) 線程可以在java虛擬機中運行的狀態,可能正在運行自己代碼,也可能沒有,這取決於操 作系統處理器。
Blocked(鎖阻 塞) 當一個線程試圖獲取一個對象鎖,而該對象鎖被其他的線程持有,則該線程進入Blocked狀 態;當該線程持有鎖時,該線程將變成Runnable狀態。
Waiting(無限 等待) 一個線程在等待另一個線程執行一個(喚醒)動作時,該線程進入Waiting狀態。進入這個狀態後是不能自動喚醒的,必須等待另一個線程調用notify或者notifyAll方法才能夠喚醒。
Timed Waiting(計時 等待) 同waiting狀態,有幾個方法有超時參數,調用他們將進入Timed Waiting狀態。這一狀態 將一直保持到超時期滿或者接收到喚醒通知。帶有超時參數的常用方法有Thread.sleep 、 Object.wait。
Teminated(被終止) 因爲run方法正常退出而死亡,或者因爲沒有捕獲的異常終止了run方法而死亡。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章