您好,登錄后才能下訂單哦!
這篇“Android的線程、多線程和線程池面試題有哪些”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Android的線程、多線程和線程池面試題有哪些”文章吧。
1)繼承Thread
類,重寫run()
方法,在run()
方法體中編寫要完成的任務 new Thread().start();
2)實現Runnable
接口,實現run()
方法 new Thread(new MyRunnable()).start();
3)實現Callable
接口MyCallable
類,實現call()
方法,使用FutureTask
類來包裝Callable
對象,使用FutureTask
對象作為Thread
對象的target
創建并啟動線程;調用FutureTask
對象的get()
方法來獲得子線程執行結束后的返回值。
FutureTask<Integer> ft = new FutureTask<Integer>(new MyCallable());new Thread(ft).start();
run()
方法只是線程的主體方法,和普通方法一樣,不會創建新的線程。
只有調用start()
方法,才會啟動一個新的線程,新線程才會調用run()
方法,線程才會開始執行。
創建Semaphore
變量,Semaphore semaphore = new Semaphore(5, true);
當方法進入時,請求一個信號,如果信號被用完則等待,方法運行完,釋放一個信號,釋放的信號新的線程就可以使用。
wait()
方法屬于Object
類,調用該方法時,線程會放棄對象鎖,只有該對象調用notify()
方法后本線程才進入對象鎖定池準備獲取對象鎖進入運行狀態。
sleep()
方法屬于Thread
類,sleep()
導致程序暫停執行指定的時間,讓出CPU
,但它的監控狀態依然保存著,當指定時間到了又會回到運行狀態,sleep()
方法中線程不會釋放對象鎖。
notify:
喚醒在此對象監視器上等待的單個線程
notifyAll():
通知所有等待該競爭資源的線程
wait:
釋放obj
的鎖,導致當前的線程等待,直接其他線程調用此對象的notify()
或notifyAll()
方法
當要調用wait()
或notify()/notifyAll()
方法時,一定要對競爭資源進行加鎖,一般放到synchronized(obj)
代碼中。當調用obj.notify/notifyAll
后,調用線程依舊持有obj
鎖,因此等待線程雖被喚醒,但仍無法獲得obj
鎖,直到調用線程退出synchronized
塊,釋放obj
鎖后,其他等待線程才有機會獲得鎖繼續執行。
1)線程執行了Thread.sleep(int millsecond)
方法,放棄CPU
,睡眠一段時間,一段時間過后恢復執行;
2)線程執行一段同步代碼,但無法獲得相關的同步鎖,只能進入阻塞狀態,等到獲取到同步鎖,才能恢復執行;
3)線程執行了一個對象的wait()
方法,直接進入阻塞態,等待其他線程執行notify()/notifyAll()
操作;
4)線程執行某些IO
操作,因為等待相關資源而進入了阻塞態,如System.in
,但沒有收到鍵盤的輸入,則進入阻塞態。
5)線程禮讓,Thread.yield()
方法,暫停當前正在執行的線程對象,把執行機會讓給相同或更高優先級的線程,但并不會使線程進入阻塞態,線程仍處于可執行態,隨時可能再次分得CPU
時間。線程自閉,join()
方法,在當前線程調用另一個線程的join()
方法,則當前線程進入阻塞態,直到另一個線程運行結束,當前線程再由阻塞轉為就緒態。
6)線程執行suspend()
使線程進入阻塞態,必須resume()
方法被調用,才能使線程重新進入可執行狀態。
使用標志位
2)使用stop()
方法,但該方法就像關掉電腦電源一樣,可能會發生預料不到的問題
3)使用中斷interrupt()
public class Thread { // 中斷當前線程 public void interrupt(); // 判斷當前線程是否被中斷 public boolen isInterrupt(); // 清除當前線程的中斷狀態,并返回之前的值 public static boolen interrupted(); }
但調用interrupt()
方法只是傳遞中斷請求消息,并不代表要立馬停止目標線程。
之所以需要同步,因為在多線程并發控制,當多個線程同時操作一個可共享的資源時,如果沒有采取同步機制,將會導致數據不準確,因此需要加入同步鎖,確保在該線程沒有完成操作前被其他線程調用,從而保證該變量的唯一一性和準確性。
由于java
的每個對象都有一個內置鎖,用此關鍵字修飾方法時,內置鎖會保護整個方法。在調用該方法前,需獲得內置鎖,否則就處于陰塞狀態。
保證變量在線程間的可見性,每次線程要訪問volatile
修飾的變量時都從內存中讀取,而不緩存中,這樣每個線程訪問到的變量都是一樣的。且使用內存屏障。
創建一個ReentrantLock
實例
lock()
獲得鎖unlock()
釋放鎖
每個線程都會保存一份該變量的副本,副本之間相互獨立,這樣每個線程都可以隨意修改自己的副本,而不影響其他線程。常用方法ThreadLocal()
創建一個線程本地變量;get()
返回此線程局部的當前線程副本變量;initialValue()
返回此線程局部變量的當前線程的初始值;set(T value)
將此線程變量的當前線程副本中的值設置為value
如AtomicInteger
,常用方法AtomicInteger(int value)
創建個有給定初始值的AtomicInteger
整數;addAndGet(int data)
以原子方式將給定值與當前值相加
例如LinkedBlockingQueue<E>
線程安全性體現在三方法:
提供互斥訪問,同一時刻只能有一個線和至數據進行操作。
JDK
中提供了很多atomic
類,如AtomicInteger\AtomicBoolean\AtomicLong
,它們是通過CAS
完成原子性。JDK
提供鎖分為兩種:synchronized
依賴JVM
實現鎖,該關鍵字作用對象的作用范圍內同一時刻只能有一個線程進行操作。另一種LOCK
,是JDK
提供的代碼層面的鎖,依賴CPU
指令,代表性是ReentrantLock
。
一個線程對主內存的修改及時被其他線程看到。
JVM
提供了synchronized
和volatile
,volatile
的可見性是通過內存屏障和禁止重排序實現的,volatile
會在寫操作時,在寫操作后加一條store
屏障指令,將本地內存中的共享變量值刷新到主內存;會在讀操作時,在讀操作前加一條load
指令,從內存中讀取共享變量。
指令沒有被編譯器重排序。
可通過volatile、synchronized、Lock
保證有序性。
我認為可以實現,比如兩個進程都讀取日歷進程數據是沒有問題,但同時寫,應該會有沖突。
可以使用共享內存實現進程間數據共享。
多線程數量的問題,一般情況下,多線程數量要等于機器CPU
核數-1
.
import java.util.ArrayList;import java.util.List;import org.apache.commons.lang3.ArrayUtils;public class Test_4 { /** * 多線程處理list * * @param data 數據list * @param threadNum 線程數 */ public synchronized void handleList(List<String> data, int threadNum) { int length = data.size(); int tl = length % threadNum == 0 ? length / threadNum : (length / threadNum + 1); for (int i = 0; i < threadNum; i++) { int end = (i + 1) * tl; HandleThread thread = new HandleThread("線程[" + (i + 1) + "] ", data, i * tl, end > length ? length : end); thread.start(); } } class HandleThread extends Thread { private String threadName; private List<String> data; private int start; private int end; public HandleThread(String threadName, List<String> data, int start, int end) { this.threadName = threadName; this.data = data; this.start = start; this.end = end; } public void run() { List<String> subList = data.subList(start, end)/*.add("^&*")*/; System.out.println(threadName+"處理了"+subList.size()+"條!"); } } public static void main(String[] args) { Test_4 test = new Test_4(); // 準備數據 List<String> data = new ArrayList<String>(); for (int i = 0; i < 6666; i++) { data.add("item" + i); } test.handleList(data, 5); System.out.println(ArrayUtils.toString(data)); } }
//測試讀取List的線程類,大概34秒package com.thread.list;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;public class Main { public static void main(String[] args) { List<String> list = new ArrayList<String>(); Map<Long,Integer> map = new HashMap<Long,Integer>(); for(int i = 0;i<1000;i++){ list.add(""+i); } int pcount = Runtime.getRuntime().availableProcessors(); long start = System.currentTimeMillis(); for(int i=0;i<pcount;i++){ Thread t = new MyThread1(list,map); map.put(t.getId(),Integer.valueOf(i)); t.start(); try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } // System.out.println(list.get(i)); } System.out.println("----"+(System.currentTimeMillis() - start)); } }//線程類package com.thread.list;import java.util.List;import java.util.Map;public class MyThread1 extends Thread { private List<String> list; private Map<Long,Integer> map; public MyThread1(List<String> list,Map<Long,Integer> map){ this.list = list; this.map = map; } @Override public void run() { int pcount = Runtime.getRuntime().availableProcessors(); int i = map.get(Thread.currentThread().getId()); for(;i<list.size();i+=pcount){ System.out.println(list.get(i)); } } }
場景:大數據List
集合,需要對List
集合中的數據同標準庫中數據進行對比,生成新增,更新,取消數據
解決方案:
List
集合分段,
動態創建線程池newFixedThreadPool
將對比操作在多線程中實現
public static void main(String[] args) throws Exception { // 開始時間 long start = System.currentTimeMillis(); List<String> list = new ArrayList<String>(); for (int i = 1; i <= 3000; i++) { list.add(i + ""); } // 每500條數據開啟一條線程 int threadSize = 500; // 總數據條數 int dataSize = list.size(); // 線程數 int threadNum = dataSize / threadSize + 1; // 定義標記,過濾threadNum為整數 boolean special = dataSize % threadSize == 0; // 創建一個線程池 ExecutorService exec = Executors.newFixedThreadPool(threadNum); // 定義一個任務集合 List<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>(); Callable<Integer> task = null; List<String> cutList = null; // 確定每條線程的數據 for (int i = 0; i < threadNum; i++) { if (i == threadNum - 1) { if (special) { break; } cutList = list.subList(threadSize * i, dataSize); } else { cutList = list.subList(threadSize * i, threadSize * (i + 1)); } // System.out.println("第" + (i + 1) + "組:" + cutList.toString()); final List<String> listStr = cutList; task = new Callable<Integer>() { @Override public Integer call() throws Exception { System.out.println(Thread.currentThread().getName() + "線程:" + listStr); return 1; } }; // 這里提交的任務容器列表和返回的Future列表存在順序對應的關系 tasks.add(task); } List<Future<Integer>> results = exec.invokeAll(tasks); for (Future<Integer> future : results) { System.out.println(future.get()); } // 關閉線程池 exec.shutdown(); System.out.println("線程任務執行結束"); System.err.println("執行任務消耗了 :" + (System.currentTimeMillis() - start) + "毫秒"); }
為對象分配存儲空間,開始構造對象,從超類到子類對static
成員初始化;超類成員變量按順序初始化,遞歸調用超類的構造方法,子類成員變量按順序初始化,子類構造方法調用。
對象至少被一個強引用持有著。
程序運行已超出對象作用域
該對象不再被強引用所持有
假設該對象重寫了finalize()
方法且未執行過,會去執行該方法。
對象運行完finalize()
方法仍處于不可達狀態,等待垃圾回收器對該對象空間進行回收。
垃圾回收器對該對象所占用的內存空間進行回收或再分配,該對象徹底消失。
static synchronized
控制的是類的所有實例訪問,不管new
了多少對象,只有一份,所以對該類的所有對象都加了鎖。限制多線程中該類的所有實例同時訪問JVM
中該類對應的代碼。
如果synchronized
修飾的是靜態方法,鎖的是當前類的class
對象,進入同步代碼前要獲得當前類對象的鎖;
普通方法,鎖的是當前實例對象,進入同步代碼前要獲得的是當前實例的鎖;
同步代碼塊,鎖的是括號里面的對象,對給定的對象加鎖,進入同步代碼塊庫前要獲得給定對象鎖;
如果兩個線程訪問同一個對象的synchronized
方法,會出現競爭,如果是不同對象,則不會相互影響。
有volatile
變量修飾的共享變量進行寫操作的時候會多一條匯編代碼,lock addl $0x0,lock
前綴的指令在多核處理器下會將當前處理器緩存行的數據會寫回到系統內存,這個寫回內存的操作會引起在其他CPU
里緩存了該內存地址的數據無效。同時lock
前綴也相當于一個內存屏障,對內存操作順序進行了限制。
synchronized
通過對象的對象頭(markword)
來實現鎖機制,java
每個對象都有對象頭,都可以為synchronized
實現提供基礎,都可以作為鎖對象,在字節碼層面synchronized
塊是通過插入monitorenter monitorexit
完成同步的。持有monitor
對象,通過進入、退出這個Monitor
對象來實現鎖機制。
NIO( New Input/ Output)
引入了一種基于通道和緩沖區的I/O
方式,它可以使用 Native
函數庫直接分配堆外內存,然后通過一個存儲在Java
堆的 DirectByteBuffer
對象作為這塊內存的引用進行操作,避免了在Java
堆和Native
堆中來回復制數據。 NIO
是一種同步非阻塞的 IO
模型。同步是指線程不斷輪詢IO
事件是否就緒,非阻塞是指線程在等待IO
的時候,可以同時做其他任務。同步的核心就是Selector,Selector
代替了線程本身輪詢IO
事件,避免了阻塞同時減少了不必要的線程消耗;非阻塞的核心就是通道和緩沖區,當IO
事件就緒時,可以通過寫道緩沖區,保證IO
的成功,而無需線程阻塞式地等待。
解決變量在多個線程間的可見性,但不能保證原子性,只能用于修飾變量,不會發生阻塞。volatile
能屏蔽編譯指令重排,不會把其后面的指令排到內存屏障之前的位置,也不會把前面的指令排到內存屏障的后面。多用于并行計算的單例模式。volatile
規定CPU
每次都必須從內存讀取數據,不能從CPU
緩存中讀取,保證了多線程在多CPU
計算中永遠拿到的都是最新的值。
互斥鎖,操作互斥,并發線程過來,串行獲得鎖,串行執行代碼。解決的是多個線程間訪問共享資源的同步性,可保證原子性,也可間接保證可見性,因為它會將私有內存和公有內存中的數據做同步。可用來修飾方法、代碼塊。會出現阻塞。synchronized
發生異常時,會自動釋放線程占有的鎖,因此不會導致死鎖現象發生。非公平鎖,每次都是相互爭搶資源。
是一個接口,而synchronized
是java
中的關鍵字,synchronized
是內置語言的實現。lock
可以讓等待鎖的線程響應中斷。在發生異常時,如果沒有主動通過unLock()
去釋放鎖,則可能造成死鎖現象,因此使用Lock
時需要在finally
塊中釋放鎖。
可重入鎖,鎖的分配機制是基于線程的分配,而不是基于方法調用的分配。ReentrantLock
有tryLock
方法,如果鎖被其他線程持有,返回false
,可避免形成死鎖。對代碼加鎖的顆粒會更小,更節省資源,提高代碼性能。ReentrantLock
可實現公平鎖和非公平鎖,公平鎖就是先來的先獲取資源。ReentrantReadWriteLock
用于讀多寫少的場合,且讀不需要互斥場景。
以上就是關于“Android的線程、多線程和線程池面試題有哪些”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。