牛啊牛啊!這篇多線程技術筆記,阿里架構師看了都說好! 程序,進程,線程 Thread生命週期 Thread類中的常用方法 創建多線程的方式 線程同步機制 線程通信 最後

程序,進程,線程

  • 程序:爲完成特定任務,使用某種語言編寫的一組指令的集合,是一段靜態的代碼。
  • 進程:程序的一次運行過程,或者是正在運行的一個程序。進程是資源分配的基本單位。
  • 線程:線程由進程進一步細化而來,是一個程序內部的一條執行路徑。線程是調度和執行的單位,每個線程擁有獨立的運行棧和程序計數器,線程開銷很小。

Thread生命週期

Thread類中的常用方法

  • start():啓動當前線程;調用當前線程的run()。
  • run():通常需要重寫Thread類中的此方法,將創建的線程要執行的操作聲明在此方法中。
  • currentThread():靜態方法,返回執行當前代碼的線程。
  • getName():獲取當前線程的名字。
  • setName():設置當前線程的名字。
  • yield():釋放當前cpu的執行權。
  • join():在線程a中調用線程b的join()方法,此時線程a就進入阻塞狀態,直到線程b完全執行完以後,線程a才結束阻塞狀態。
  • sleep(long millitime):讓當前線程“睡眠”指定的millitime毫秒。在指定的millitime毫秒時間內,當前線程是阻塞狀態。
  • isAlive():判斷當前線程是否存活。
  • getPriority():獲取線程的優先級。
  • setPriority(int p):設置線程的優先級。
  • 線程的優先級有以下三種,MAX_PRIORITY:10;MIN _PRIORITY:1;NORM_PRIORITY:5。

創建多線程的方式

繼承Thread類

步驟:

  • 創建一個繼承於Thread類的子類;
  • 重寫Thread類中的run()方法,將次線程執行的操作聲明在run()方法中。
  • 創建Thread類的子類對象。
  • 通過此對象調用start()。
    舉例:三個窗口進行賣票(存在線程安全問題)
class MThread extends Thread{
    private static int tickets = 100;
    @Override
    public void run() {
        while(true){
            if(tickets > 0){
                System.out.println(getName() + "賣票,票號爲:" + tickets--);
            }else{
                break;
            }
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {
        MThread t1 = new MThread ();
        MThread t2 = new MThread ();
        MThread t3 = new MThread ();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

說明:侷限於類的單繼承性。

實現Runnable接口

步驟:

  • 創建一個實現Runnable接口的類。
  • 實現類去實現Runnable中的抽象方法:run()。
  • 創建實現類的對象。
  • 將此對象作爲參數傳遞到Thread類的構造器中,創建Thread類的對象。
  • 通過Thread類的對象調用start()。
    舉例:三個窗口進行賣票(存在線程安全問題)
class MThread implements Runnable{
   private int tickets = 100;
   @Override
   public void run() {
       while(true){
           if(tickets > 0){
               System.out.println(Thread.currentThread().getName() + "賣票,票號爲:" + tickets--);
           }else{
               break;
           }
       }
   }
}
public class ThreadTest {
   public static void main(String[] args) {
       MThread m = new MThread();
       Thread t1 = new Thread(m);
       Thread t2 = new Thread(m);
       Thread t3 = new Thread(m);
       t1.setName("窗口1");
       t2.setName("窗口2");
       t3.setName("窗口3");
       t1.start();
       t2.start();
       t3.start();
   }
}

說明:不會侷限於類的單繼承性;適合處理多個線程共享數據的情況。

實現Callable接口

步驟:

  • 創建一個實現Callable的實現類。
  • 實現call方法,將此線程需要執行的操作聲明在call()中。
  • 創建Callable接口實現類的對象。
  • 將此Callable接口實現類的對象作爲參數傳遞到FutureTask構造器中,創建FutureTask的對象。
  • 將FutureTask的對象作爲參數傳遞到Thread類的構造器中,創建Thread對象,並調用start()。
    舉例:遍歷100以內的偶數,並且計算他們的和
class MThread implements Callable{
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 2; i < 101; i+=2) {
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}


public class ThreadTest {
    public static void main(String[] args) {
        MThread thread = new MThread ();
        FutureTask futureTask = new FutureTask(thread);
        new Thread(futureTask).start();
        try { 
            //get()返回值即爲FutureTask構造器參數Callable實現類重寫的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("總和爲:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}


說明:call()方法有返回值,並且可以拋出異常;callable支持泛型。

線程池

步驟:

  • 提供指定線程數量的線程池。
  • 執行指定的線程的操作。需要提供實現Runnable接口或Callable接口實現類的對象。
  • 關閉線程池。
    舉例:兩個線程遍歷100以內的奇偶數
class NumberThread implements Runnable{

    @Override
    public void run() {
        for(int i = 1;i < 101; i+=2){
             System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

class NumberThread1 implements Runnable{

    @Override
    public void run() {
        for(int i = 2;i < 101; i+=2){
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

public class ThreadPool {
    public static void main(String[] args) { 
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        service.execute(new NumberThread());//適用於Runnable
        service.execute(new NumberThread1());//適用於Runnable

        //service.submit(Callable callable);//使用於Callable
        service.shutdown();
    }
}


說明:提高了響應速度;降低資源消耗;便於線程管理。

線程同步機制

解決線程安全問題

同步代碼塊

synchronized(對象){//需要被同步的代碼}
舉例:三個窗口賣票

class Windows extends Thread{
    private static int tickets = 100;
    @Override
    public void run() {
        while(true){
            synchronized(Windows.class){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(tickets > 0){
                    System.out.println(getName() + "賣票,票號爲:" + tickets--);
                }else{
                    break;
                }
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Windows t1 = new Windows();
        Windows t2 = new Windows();
        Windows t3 = new Windows();
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}


同步方法

將synchronized放到方法的聲明中。
舉例:三個窗口賣票。

class Windows implements Runnable {
    private int tickets = 100;
    @Override
    public void run() {
        while(true){
            show();
        }
    }
    private synchronized void show(){
        if(tickets > 0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "賣票,票號爲:" + tickets--);
        }
    }

}
public class Test{
    public static void main(String[] args) {
        Windows t = new Windows();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        Thread t3 = new Thread(t);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();t2.start();t3.start();
    }
}


Lock

class A{
    private final ReentrantLock lock = new ReentrantLock();
    public void m(){
        lock.lock();
        try{
            //保證線程安全的代碼
        }
        finally{
            lock.unlock();
        }
    }
}

舉例:三個窗口賣票

class Window implements Runnable{
    private int ticket = 100;
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            try{
                lock.lock();
                if(ticket > 0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "賣票,票號爲:" + ticket--);
                }
            }finally {
                lock.unlock();
            }
        }
    }
}
public class LockTest {
    public static void main(String[] args) {
        Window w = new Window();
        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);
        t1.setName("線程1");t2.setName("線程2");t3.setName("線程3");
        t1.start();t2.start();t3.start();
    }
}


synchronized和lock對比:

  • lock是顯示鎖,手動開啓和關閉;synchronized是隱式鎖,出了作用域自動釋放。
  • lock只有代碼塊鎖;synchronized有代碼塊鎖和方法鎖。
  • 使用lock鎖,JVM將花費較少的時間來調度線程,性能更好;並且具有更好的擴展性。

線程通信

三個方法

  • wait():一旦執行此方法,當前線程就進入阻塞狀態,並釋放同步監視器。
  • )notify():一旦執行此方法,就會喚醒被wait的一個線程。如果有多個線程被wait,就喚醒優先級高的那個。
  • notifyAll():一旦執行此方法,就會喚醒所有被wait的線程。

說明

  • wait(),notify(),notifyAll()三個方法必須使用在同步代碼塊或同步方法中。
  • wait(),notify(),notifyAll()三個方法的調用者必須是同步代碼塊或同步方法中的同步監視器。否則,會出現IllegalMonitorStateException異常。
  • wait(),notify(),notifyAll()三個方法是定義在java.lang.Object類中。

舉例:生產者消費者問題

class Clerk{
    private int productCount = 0;
    public synchronized void produceProduct(){
        if(productCount < 20){
            productCount ++;
            System.out.println(Thread.currentThread().getName() + "生產第" + productCount + "個產品");
            notify();
        }else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void consumeProduct() {
        if(productCount > 0){
            System.out.println(Thread.currentThread().getName() + "消費第" + productCount + "個產品");
            productCount --;
            notify();
        }else {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Productor extends Thread{
    private Clerk clerk;

    public Productor(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println("生產者" + Thread.currentThread().getName() + "開始生產");
        while(true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
}
class Consumer extends Thread{
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println("消費者" + Thread.currentThread().getName() + "取走產品");
        while(true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}
public class ProductTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Productor p1 = new Productor(clerk);
        p1.setName("生產者1");
        Consumer c1 = new Consumer(clerk);
        c1.setName("消費者1");
        p1.start();c1.start();
    }
}


最後

在文章的最後作者爲大家整理了很多資料!包括java核心知識點+全套架構師學習資料和視頻+一線大廠面試寶典+面試簡歷模板+阿里美團網易騰訊小米愛奇藝快手嗶哩嗶哩面試題+Spring源碼合集+Java架構實戰電子書等等!

全部免費分享給大家,歡迎關注公衆號:前程有光領取!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章