1. 程式人生 > 程式設計 >新手場景Java執行緒相關問題及解決方案

新手場景Java執行緒相關問題及解決方案

一、建立執行緒方式

a. 繼承執行緒類( new Thread),重寫run方法;

public class MyThread extends Thread{//繼承Thread類
  public void run(){
  //重寫run方法
  }
}
public class Main {
public static void main(String[] args){
    new MyThread().start();//建立並啟動執行緒
  }
}

b. 實現runnable介面,將runnable物件傳入Thread類;

public class MyThread2 implements Runnable {//實現Runnable介面
  public void run(){
  //重寫run方法
  }
}
public class Main {
  public static void main(String[] args){
    //建立並啟動執行緒
    MyThread2 myThread=new MyThread2();
    Thread thread=new Thread(myThread);
    thread().start();
    //或者  new Thread(new MyThread2()).start();
  }
}

c. 使用執行緒池的方式,提交runnable或callable任務;

public class Main {
  public static void main(String[] args){
   MyThread3 th=new MyThread3();
   //使用Lambda表示式建立Callable物件
   //使用FutureTask類來包裝Callable物件
   FutureTask<Integer> future=new FutureTask<Integer>(
    (Callable<Integer>)()->{
      return 5;
    }
   );
   new Thread(task,"有返回值的執行緒").start();//實質上還是以Callable物件來建立並啟動執行緒
   try{
    System.out.println("子執行緒的返回值:"+future.get());//get()方法會阻塞,直到子執行緒執行結束才返回
   }catch(Exception e){
    ex.printStackTrace();
   }
  }
}

d. 推薦使用第三種方式。高效,資源可控;

二、什麼是執行緒同步?執行緒同步什麼時候用?

1)什麼是執行緒同步;

即當有一個執行緒在對記憶體進行操作時,其他執行緒都不可以對這個記憶體地址進行操作,直到該執行緒完成操作, 其他執行緒才能對該記憶體地址進行操作,而其他執行緒又處於等待狀態;

2)執行緒同步在什麼時候用(賣火車票,飛機票,取錢);

簡單的說,同步就是防止多個執行緒訪問同一個物件,造成資料不安全;執行緒的同步意味安全,譬如你去取錢 ,你的執行語句和我用的要是相同物件 ,你要在卡上扣除的錢數和銀行卡里面要有這麼多錢才能扣除;

三、什麼是執行緒安全;

1)所謂執行緒安全,是多個執行緒併發執行的情況下結果總是跟單執行緒執行的結果一致,邏輯上不會出現錯誤;

2)什麼情況下會出現執行緒安全問題?

多個執行緒同時操作同一份資料,常常會導致執行緒安全問題。比如:全域性的變數,靜態變數,同一條資料的資料庫操作等;

區域性變數,通常不會存線上程安全問題。

3)常見的解決執行緒安全的方式:

1)避免使用全域性的變數,將全域性的變數定義為區域性變數。

2)加同步鎖,使得執行緒同步。

a. Synchronized 同步關鍵字,可以加在方法和程式碼塊上面;

/**
   * synchronized新增到方法上面,使方法變成同步方法
   * 如果是靜態方法,鎖住的是class
   * 如果是普通方法,鎖住的this,當前物件 synchronized(this)
   * 多個執行緒鎖住的物件是同一個物件才能夠同步,每個類都有當前物件
   */

  public static synchronized void salTicket(){
    if (ticketNum > 0) {
      try {
        Thread.sleep(100);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + "買到第" + ticketNum-- + "張票");
      System.out.println(Thread.currentThread().getName() + "買票完成");
    }else {
      System.out.println("票已經售完,"+Thread.currentThread().getName() + "未買到票");
    }
  }

b. 多個執行緒方法是否同步,需要判斷多個執行緒是否共用同一把鎖;

/**
   * 同步程式碼塊
   */
  public void salTicket2() {
    synchronized (this) {
      //()中指定鎖物件,this表示當前物件,多個執行緒使用同一個物件呼叫該方法時,是同步的
      //如果指定為 class,則該類的任意物件呼叫該方法都是同步的
      if (ticketNum > 0) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "買到第" + ticketNum-- + "張票");
        System.out.println(Thread.currentThread().getName() + "買票完成");
      } else {
        System.out.println("票已經售完," + Thread.currentThread().getName() + "未買到票");
      }
    }
  }

c. ReentrantLock 物件的lock 方法進行加鎖,unLock進行解鎖。Unlock必須放在finally中。確保能夠最終釋放鎖;

//鎖物件 private static ReentrantLock lock=new ReentrantLock();
 /**
   * 使用lock物件進行同步,多個執行緒使用的是同一個lock物件,才會是同步的
   */
  public void salTicket3() {
    lock.lock();//加鎖
      if (ticketNum > 0) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }finally {
          lock.unlock();//釋放鎖,必須放在finally程式碼塊中,以確保能夠釋放鎖
        }
        System.out.println(Thread.currentThread().getName() + "買到第" + ticketNum-- + "張票");
        System.out.println(Thread.currentThread().getName() + "買票完成");
      } else {
        System.out.println("票已經售完," + Thread.currentThread().getName() + "未買到票");
      }
  }

3)資料庫操作的話,也可以使用樂觀鎖或悲觀鎖的方式

4)Springmvc是如何解決執行緒安全問題的?

Springmvc的資料接收和傳遞都是方法級別的,使用區域性變數來接收和傳遞,所以不存線上程安全問題。

四、Wait 和notify

執行緒間通訊的一種機制。用於手動控制執行緒之間的切換。在同步程式碼中的鎖物件呼叫。可以同時通過共享記憶體物件,來實現資料的傳遞;

  • Wait 使執行緒進入阻塞狀態,並釋放鎖資源;
  • Notify 隨機喚醒一個因wait進入阻塞狀態的執行緒;
  • NotifyALL 喚醒所有的因wait進入阻塞狀態的執行緒;

3)多執行緒實現,怎麼用?

a、將單個大的任務拆分成多個小任務,使用多執行緒去執行;

多執行緒的效率不一定比單執行緒的效率高;

通過執行緒池建立執行緒,通過實現runnable(無返回值)或callable(有返回值)介面來定義任務;

通過執行緒池的submit invoke invokeAll 等方法來執行任務;

b、使用執行緒非同步完成某些任務,提高併發響應的能力,或讓執行緒週期性的執行某些任務;

c、四種執行緒池的特點及其建立;

Java通過Executors提供四種執行緒池,分別為:

1)newCachedThreadPool建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。

2)newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。

3)newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行。

4)newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO,LIFO,優先順序)執行。

//jdk中提供的四種快速建立執行緒池的方式
    //1.定長執行緒池
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
    //2.快取執行緒池
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    //3.週期執行緒池,可用來實現定時任務
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
    //4.單執行緒執行緒池
    ExecutorService executorService = Executors.newSingleThreadExecutor();

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。