1. 程式人生 > >面試關於多執行緒

面試關於多執行緒

1. 我們來溫習下多執行緒的實現方式

1 繼承 thread

public class myThread extends Thread{
	@override
	run{
		system.out.println("你好我是執行緒");
	}
}

啟動執行緒

myThread  mt =  new myThread ();
mt.start();

2 實現介面Runnable

public class myThread implment Runnable{
	@override
	run{
		system.out.println("你好我是執行緒");
	}
}

啟動執行緒

myThread  mt =  new myThread ();
Thread tt = new Thread (mt);
tt.start();

3 實現callable

public class mycall implement callable<Integer>{
	@override
	public Integer call{
		   	system.out.println("你好我是執行緒");
		   	retrun 0;
	}
}

啟動執行緒

mycall  mc = new mycall();
FutureTask ft = new FutureTask(mc);
Thread thread =new Thread(ft);
thread.start();
system.out.println(ft.get());

4 使用執行緒池
啟動方法

ExecutorService theadPool = Executor.newFixedThreadPool(4);

ArrayList<Future<Status>> arrayList=new ArrayList<Future<Status>>();

for (int i = 1; i <=4; i++) {
			mycall  mc = new mycall();
			Future<Integer> ft = 	theadPool.submit(mc);
			arrayList.add(ft);
			}
			theadPool.shutdown;
			
for (Future<Status> future2 : arrayList) {
			System.err.println(future2.get().name());
		}

2 . 你有沒有使用concurrent工具類

Java 5 添加了一個新的包到 Java 平臺,java.util.concurrent 包
點這裡
介面Executor Runnable 是中有run方法

public interface Executor {
  void execute(Runnable command);//執行一個Runnable物件
  }

ExecutorService 是Executor介面的實現類,其子類有AbstractExecutorService,SchedualedExecutorService

Executors 是一個工具類,提供方法返回ExecutorService來建立執行緒池
點這裡
Executors 所建立的執行緒池實際使用ExecutorService的子類ThreadPoolExecutor,我們來看個例子

  public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

所以Executors 只是一個幫我們填好引數的呼叫ThreadPoolExecutor的工具類,看到一些文章中阿里釋出的java守則中建議不使用Executors來建立執行緒池,所以我們等下再來認真學習下ThreadPoolExecutor內的引數。

Executors 的四種執行緒池

 newCachedThreadPool  	  建立有快取的執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。
 newFixedThreadPool    	  建立一個定長的執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
 newScheduledThreadPool   建立一個定長執行緒池,支援定時及週期性任務執行。
 newSingleThreadExecutor  建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。

例子:

ExecutorService theadpool = Exectors.newFixThreadPool(5);
Mycall mycall = new Mycall();
Future <Status> future = theadpool.submit(mycall );

執行緒池的5種狀態:

    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;
  • RUNNING 執行
    狀態說明:執行緒池處在RUNNING狀態時,能夠接收新任務,以及對已新增的任務進行處理。
  • SHUTDOWN 停止執行
    狀態說明:執行緒池處在SHUTDOWN狀態時,不接收新任務,但能處理已新增的任務。
  • STOP
    狀態說明:執行緒池處在STOP狀態時,不接收新任務,不處理已新增的任務,並且會中斷正在處理的任務。
  • TIDYING
    當執行緒池在STOP狀態下,執行緒池中執行的任務為空時,就會由STOP -> TIDYING。
  • TERMINATED
    執行緒池處在TIDYING狀態時,執行完terminated()之後,就會由 TIDYING -> TERMINATED。

關閉執行緒池:
shutdown() 停止加入新的執行緒任務
shutdownNow() 中斷在執行的執行緒任務,清空快取佇列

執行緒池的工作過程如下:

  1. 執行緒池剛建立時,裡面沒有一個執行緒。任務佇列是作為引數傳進來的。不過,就算佇列裡面有任務,執行緒池也不會馬上執行它們。
  2. 當呼叫 execute() 方法新增一個任務時,執行緒池會做如下判斷:
    1、 如果正在執行的執行緒數量小於 corePoolSize,那麼馬上建立執行緒執行這個任務;
    2 、 如果正在執行的執行緒數量大於或等於 corePoolSize,那麼將這個任務放入佇列;
    3 、如果這個時候佇列滿了,而且正在執行的執行緒數量小於maximumPoolSize,那麼建立執行緒執行這個任務;
    4 、如果佇列滿了,切正在執行的執行緒數量大於等於maximumPoolSize,那麼就執行拒絕策略。
  3. 當一個執行緒完成任務時,它會從佇列中取下一個任務來執行。
  4. 當一個執行緒無事可做,超過一定的時間(keepAliveTime)時,執行緒池會判斷,如果當前執行的執行緒數大於 corePoolSize,那麼這個執行緒就被停掉。所以執行緒池的所有任務完成後,它最終會收縮到 corePoolSize 的大小。

3 如何控制執行緒執行順序

1 Executors.newSingleThreadExecutor
newSingleThreadExecutor產生一個單執行緒的執行緒池,而這個執行緒池的底層原理就是一個先進先出(FIFO)的佇列。按照加入執行緒池的順序執行。

ExecutorService theadpool = Exectors.newSingleThreadExecutor;
Mycall mycall = new Mycall();
Future <Status> future = theadpool.submit(mycall );

2 join
使用執行緒的join方法,該方法的作用是“等待執行緒執行結束”,即join()方法後面的程式碼塊都要等待現場執行結束後才能執行。 實質是將主執行緒阻塞,等到執行緒執行完。

myThread my = new myThread();
Thread thread = new Thread(my);
thread.start();
thread.join;

4 ThreadPoolExecutor

點這裡更好的理解執行緒池
ThreadPoolExecutor是ExecutorService的具體實現類,建立執行緒池一般使用這個類的方法,
例如:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

我們來解釋下其中的引數:

  • corePoolSize:執行緒池的核心執行緒數,執行緒池中執行的執行緒數也永遠不會超過 corePoolSize 個,預設情況下可以一直存活。可以通過設定allowCoreThreadTimeOut為True,此時 核心執行緒數就是0,此時
  • keepAliveTime控制所有執行緒的超時時間。
  • maximumPoolSize:執行緒池允許的最大執行緒數;
  • keepAliveTime: 指的是空閒執行緒結束的超時時間。當一個執行緒無事可做,超過keepAliveTime時,執行緒池會判斷如果當前執行的執行緒數大約corePoolSize,那麼這個執行緒就會被停掉。
  • unit :是一個列舉,表示 keepAliveTime 的單位;
  • workQueue:表示存放任務的BlockingQueue<Runnable佇列。

ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;

ArrayBlockingQueue
ArrayBlockingQueue是一個用陣列實現的有界阻塞佇列。此佇列按照先進先出(FIFO)的原則對元素進行排序。
LinkedBlockingQueue
LinkedBlockingQueue是一個用連結串列實現的有界阻塞佇列。此佇列的預設和最大長度為Integer.MAX_VALUE。此佇列按照先進先出的原則對元素進行排序。
SynchronousQueue
SynchronousQueue是一個不儲存元素的阻塞佇列。每一個put操作必須等待一個take操作,否則不能繼續新增元素。

瞭解了佇列的基本知識,我們再回頭看下四種執行緒池

  • newCachedThreadPool
    使用佇列SynchronousQueue,多大執行緒數為Integer.MAX_VALUE,超時時間為60L, 時間單位為TimeUnit.SECONDS,
 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  • newFixedThreadPool
    使用佇列LinkedBlockingQueue
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  • newScheduledThreadPool
    使用佇列DelayedWorkQueue 實現類是ScheduledThreadPoolExecutor,核心執行緒大小corePoolSize
public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
  • newSingleThreadExecutor
    使用LinkedBlockingQueue佇列實現,最大執行緒數為1
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

下面只是簡單的寫一下,具體的事例有時間了再補上


5 執行緒間通訊及同步

  • synchronized
    可以鎖住資源不被其他執行緒修改
  • wait/notify機制
    wai/notify 是object的方法

6 阻塞佇列和非阻塞佇列

點這裡
實現佇列的阻塞實質上對佇列中的資料lock
而非阻塞佇列沒有lock
使用阻塞佇列與非阻塞佇列實現生產者和消費者。

7 執行緒池的拒絕策略

這個好
有四種拒絕策略

  • 1 DiscardPolicy
    放棄要加入佇列的任務
  • 2 DiscardOldestPolicy
    放棄在佇列最前端的任務
  • 3 RejectedExecutionException
    丟擲異常
  • 4 CallerRunsPolicy
    直接執行execute

如何停止正在執行的執行緒

點這裡
1 使用stop
2 使用interrupt
3 使用退出標識,使得執行緒正常退出

那麼如何停止執行緒池中的執行緒呢
點這裡
shutdown
shutdownNow