新手瞭解java 多線程基礎知識

一、線程的生命周期

JDK中用Thread.State類定義瞭線程的幾種狀態:

要想實現多線程,必須在主線程中創建新的線程對象。Java語言使用 Thread類及其子類的對象來表示線程,在它的一個完整的生命周期中通常 要經歷如下的五種狀態:

  • 新建:當一個Thread類或其子類的對象被聲明並創建時,新生的線程 對象處於新建狀態;
  • 就緒:處於新建狀態的線程被start()後,將進入線程隊列等待CPU時間 片,此時它已具備瞭運行的條件,隻是沒分配到CPU資源;
  • 運行:當就緒的線程被調度並獲得CPU資源時,便進入運行狀態, run()方法定義瞭線程的操作和功能;
  • 阻塞:在某種特殊情況下,被人為掛起或執行輸入輸出操作時,讓出 CPU 並臨時中止自己的執行,進入阻塞狀態;
  • 死亡:線程完成瞭它的全部工作或線程被提前強制性地中止或出現異常 導致結束。 在上面五個階段中,隻有新建和死亡是不可重復,其它幾個狀態都可能改變。

在這裡插入圖片描述

註意:

1.新建和死亡狀態隻能有一次;

2.運行狀態隻能是從就緒狀態變過來的;

3.阻塞狀態不能直接變為運行狀態,需要通過就緒狀態;

4.當一個運行狀態的線程調用yield()方法後,就會變為就緒狀態;

5.當一個運行狀態的線程調用sleep()、等待同步鎖方法、wait()方法或 join()方法時,就會處理阻塞狀態;

6.當一個阻塞狀態的線程調用notify()/notifyAll()方法,或者sleep()方法 結束,再或者獲得同步鎖,或者join()線程執行完畢就可以變為就緒狀 態的線程

7.當一個線程執行過多後就處理死亡狀態,即線程生命周期結束。

二、線程同步

1、為什麼要有線程同步

為瞭解決線程安全問題,在多線程下,多個線程對一個數據進行修改時,可能會產生數據出錯的問題,所以我們就需要通過線程同步的方法來解決問題。

Java中的線程同步實現方式:

2、synchronized

2.1同步代碼塊

示例:

public class MyRunnableTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();

        Thread thread = new Thread(myRunnable,"A");
        Thread thread1 = new Thread(myRunnable,"B");
        Thread thread2 = new Thread(myRunnable,"C");
        Thread thread3 = new Thread(myRunnable,"D");
        Thread thread4 = new Thread(myRunnable,"E");
        thread.start();
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();

    }
    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            synchronized (Thread.class){
                System.out.println(Thread.currentThread().getName()+"在過山洞");
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

註意: 同步鎖可以是任意對象,但該對象必須唯一; 同步代碼塊所在位置也很重要,同步代碼塊需要把引起數據問題的所有代碼都包裹起,不能多裹,也不能少裹。 我們通常都是使用字節碼文件來作為同步鎖。

2.2同步方法

示例:

public class MyRunnableTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();

        Thread thread = new Thread(myRunnable,"A");
        Thread thread1 = new Thread(myRunnable,"B");
        Thread thread2 = new Thread(myRunnable,"C");
        Thread thread3 = new Thread(myRunnable,"D");
        Thread thread4 = new Thread(myRunnable,"E");
        thread.start();
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();

    }
    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            test()
        }
 public synchronized void test(){
        System.out.println(Thread.currentThread().getName()+"在過山洞");
        try {
             Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

**註意:**當使用同步方法來處理多線程的共享數據問題,如果是靜態方法,使用的是類名.class方式,如果是非靜態方法,使用的是this。

3、Lock鎖

使用Lock.lock()進行加鎖操作,然後使用Lock.unlock()方法來進行 解鎖。

Lock是一個接口,不能直接實例化,需要使用子類來實例化,通常使用的 子類是ReentrantLock。

public class MyRunnableTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable,"A");
        Thread thread1 = new Thread(myRunnable,"B");
        Thread thread2 = new Thread(myRunnable,"C");
        Thread thread3 = new Thread(myRunnable,"D");
        Thread thread4 = new Thread(myRunnable,"E");
        thread.start();
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();

    }
}
public class MyRunnable implements Runnable{
    Lock lock = new ReentrantLock();
        lock.lock();
        System.out.println(Thread.currentThread().getName()+"在過山洞");
        try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
            lock.unlock();
        }
        }
    }

說明:

在需要作同步操作的代碼塊之前需要使用Lock.lock()方法來作加鎖處 理;在同步操作的代碼塊之後,增加finally語句塊來釋放鎖,釋放鎖的方法為Lock.unlock()方法;一定要確保鎖能釋放,否則就是死鎖;

Lock比synchronized更輕量級,功能更強大,如果可以盡量使用Lock。

四.基本概念

程序、進程、線程

  • 程序(program)是為完成特定任務、用某種語言編寫的一組指令的集 合。即指一段靜態的代碼,靜態對象。
  • 進程(process)是程序的一次執行過程,或是正在運行的一個程序。是 一個動態的過程:有它自身的產生、存在和消亡的過程——具有生命 周期。可以理解為一個正在運行的軟件。
  • 線程(thread),進程可進一步細化為線程,是一個程序內部的一條執行 路徑。可以理解為一個軟件的功能。

多線程程序的優點:

  • 提高應用程序的響應。對圖形化界面更有意義,可增強用戶體驗。
  • 提高計算機系統CPU的利用率。
  • 改善程序結構。將既長又復雜的進程分為多個線程,獨立運行,利於理 解和修改。

五.多線程的創建

​ 在Java中我們可以使用java.lang.Thread類來實現 ,要想我們的類具有多線程的功能,需要讓我們的類去繼承Thread類,然後重寫run()方法,並調用 start()方法來啟動多線程。

示例 1:

public class MyThread extends Thread{
    public void run(){
        for (int i = 0; i < 50; i++) {
            System.out.println("MyThread:"+i);
        }
    }
}
public class MyThreadTest{
    public static void main(String[] args){
        Thread t1 = new MyThread();
        t1.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("world=====" + i);
        }
    }
}

說明:創建一個Java類繼承Thread類,並重寫父類Thread中的run方法,在run方法中寫具體的多線程業務。創建線程類的對象,並調用start方法啟動多線程。

**註意:**多線程的啟動調用的是start方法,在jvm底層中start方法內部會調用run方法。

一個對象隻需要調用一次start()方法,如果多次調用則會拋出異常“IllegalThreadStateException”。

示例 2:

public class MyRunnable implements Runnable {
    public void run() {
        for (int i = 0; i < 50 ; i++) {
            System.out.println( "MyRunnable:"+i);
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread( new MyRunnable());
        thread.start();// 隻有調用Thread類中的start()方法才可以實現多線程
            for (int i = 0; i < 50; i++) {
                System.out.println("main:" + i);
            }
    }
}

說明:

  • 編寫一個類,實現Runnable接口;
  • 重寫run()方法;
  • 根據Runnable子類對象來創建Thread對象;
  • 通過Thread對象調用start()方法來啟動多線程;

總結:

  • 繼承Thread:重寫run()方法,業務代碼在run()中。
  • 實現Runnable:線程代碼存在接口的子類的run方法。
  • 通過Callable和線程池的方式創建線程。

**提示:**在應用中我們如果可以使用Runable接口那麼就盡量使用,這樣可以避免Java單繼承的局限。

六.Thread類方法介紹

1)currentThread():返回當前正在執行的線程對象的引用。

2)getName():返回當前線程的名稱

3)isAlive():判斷當前線程是否存活

4)isDeaomon():判斷線程是否為守護線程

5)join():在當前線程中引入另一個線程,而當前線程會被阻塞

6)sleep():讓當前線程進入睡眠狀態

7)yield():讓當前線程放棄CPU的執行權,重新進入排隊,與其他線程平等爭奪CPU執行。

8)interrupt() 中斷線程 9)interrupted() 如果當前線程已經中斷,則返回 true;否則返回 false。

示例:

public class ThreadTest {
    public static void main(String[] args) {
        //創建線程並開啟線程1
        Thread thread = new MyThread();
        thread.start();
        //創建線程並開啟線程2
        Thread thread1 = new Thread(new MyRunnable());
        thread1.start();
        //創建線程並開啟線程3
        MyCallable myCallable = new MyCallable();
        FutureTask futureTask = new FutureTask(myCallable);
        new Thread(futureTask).start();

        for (int i = 0; i < 100; i++) {
            if (i== 50){
                //在main線程中當i=50加如thread線程,會在thread執行完後才會繼續執行main線程剩下的
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main:"+i);
        }
    }
}

public class MyThread extends Thread{
    public void run(){
        for (int i = 0; i < 100; i++) {
            // void interrupt() 中斷線程 可以理解為線程中斷狀態有 true | false,每一次調用就是修改狀態為true
            //static boolean interrupted() 如果當前線程已經中斷,則返回 true;否則返回 false。
            Thread.currentThread().interrupt();
            if (Thread.interrupted()){
                System.out.println("Thread interrupted");
            }
            // static currentThread()  返回對當前正在執行的線程對象的引用
            //String getName()   返回該線程的名稱。
            //Thread.currentThread().getName()  獲取當前線程對象的名稱
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyRunnable"+i);
            //每次執行完打印讓線程休眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("MyCallable:"+i);
        }
        return null;
    }
}

總結

本篇文章就到這裡瞭,希望對你有所幫助,也希望你能夠多多關註WalkonNet的更多內容!

推薦閱讀: