成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?【后25題】(二)

caozhijian / 3242人閱讀

摘要:大多數(shù)待遇豐厚的開(kāi)發(fā)職位都要求開(kāi)發(fā)者精通多線程技術(shù)并且有豐富的程序開(kāi)發(fā)調(diào)試優(yōu)化經(jīng)驗(yàn),所以線程相關(guān)的問(wèn)題在面試中經(jīng)常會(huì)被提到。掌握了這些技巧,你就可以輕松應(yīng)對(duì)多線程和并發(fā)面試了。進(jìn)入等待通行準(zhǔn)許時(shí),所提供的對(duì)象。

最近看到網(wǎng)上流傳著,各種面試經(jīng)驗(yàn)及面試題,往往都是一大堆技術(shù)題目貼上去,而沒(méi)有答案。

不管你是新程序員還是老手,你一定在面試中遇到過(guò)有關(guān)線程的問(wèn)題。Java語(yǔ)言一個(gè)重要的特點(diǎn)就是內(nèi)置了對(duì)并發(fā)的支持,讓Java大受企業(yè)和程序員的歡迎。大多數(shù)待遇豐厚的Java開(kāi)發(fā)職位都要求開(kāi)發(fā)者精通多線程技術(shù)并且有豐富的Java程序開(kāi)發(fā)、調(diào)試、優(yōu)化經(jīng)驗(yàn),所以線程相關(guān)的問(wèn)題在面試中經(jīng)常會(huì)被提到。
在典型的Java面試中, 面試官會(huì)從線程的基本概念問(wèn)起

如:為什么你需要使用線程, 如何創(chuàng)建線程,用什么方式創(chuàng)建線程比較好(比如:繼承thread類(lèi)還是調(diào)用Runnable接口),然后逐漸問(wèn)到并發(fā)問(wèn)題像在Java并發(fā)編程的過(guò)程中遇到了什么挑戰(zhàn),Java內(nèi)存模型,JDK1.5引入了哪些更高階的并發(fā)工具,并發(fā)編程常用的設(shè)計(jì)模式,經(jīng)典多線程問(wèn)題如生產(chǎn)者消費(fèi)者,哲學(xué)家就餐,讀寫(xiě)器或者簡(jiǎn)單的有界緩沖區(qū)問(wèn)題。僅僅知道線程的基本概念是遠(yuǎn)遠(yuǎn)不夠的, 你必須知道如何處理死鎖,競(jìng)態(tài)條件,內(nèi)存沖突和線程安全等并發(fā)問(wèn)題。掌握了這些技巧,你就可以輕松應(yīng)對(duì)多線程和并發(fā)面試了。
許多Java程序員在面試前才會(huì)去看面試題,這很正常。

因?yàn)槭占嬖囶}和練習(xí)很花時(shí)間,所以我從許多面試者那里收集了Java多線程和并發(fā)相關(guān)的50個(gè)熱門(mén)問(wèn)題。

關(guān)注微信公眾號(hào) "搜云庫(kù)" 獲取最新文章 【福利】公眾號(hào)后臺(tái)回復(fù) “進(jìn)群” 拉你進(jìn)微信【技術(shù)分享群】 【福利】在里面你可以認(rèn)識(shí)到很多搞技術(shù)大佬,免費(fèi)提問(wèn),互相學(xué)習(xí)

下面是Java線程相關(guān)的熱門(mén)面試題,你可以用它來(lái)好好準(zhǔn)備面試。

【前25題】想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)

前25題想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)

什么是線程?

什么是線程安全和線程不安全?

什么是自旋鎖?

什么是Java內(nèi)存模型?

什么是CAS?

什么是樂(lè)觀鎖和悲觀鎖?

什么是AQS?

什么是原子操作?在Java Concurrency API中有哪些原子類(lèi)(atomic classes)?

什么是Executors框架?

什么是阻塞隊(duì)列?如何使用阻塞隊(duì)列來(lái)實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模型?

什么是Callable和Future?

什么是FutureTask?

什么是同步容器和并發(fā)容器的實(shí)現(xiàn)?

什么是多線程??jī)?yōu)缺點(diǎn)?

什么是多線程的上下文切換?

ThreadLocal的設(shè)計(jì)理念與作用?

ThreadPool(線程池)用法與優(yōu)勢(shì)?

Concurrent包里的其他東西:ArrayBlockingQueue、CountDownLatch等等。

synchronized和ReentrantLock的區(qū)別?

Semaphore有什么作用?

Java Concurrency API中的Lock接口(Lock interface)是什么?對(duì)比同步它有什么優(yōu)勢(shì)?

Hashtable的size()方法中明明只有一條語(yǔ)句”return count”,為什么還要做同步?

ConcurrentHashMap的并發(fā)度是什么?

ReentrantReadWriteLock讀寫(xiě)鎖的使用?

CyclicBarrier和CountDownLatch的用法及區(qū)別?

LockSupport工具?

Condition接口及其實(shí)現(xiàn)原理?

Fork/Join框架的理解?

wait()和sleep()的區(qū)別?

線程的五個(gè)狀態(tài)(五種狀態(tài),創(chuàng)建、就緒、運(yùn)行、阻塞和死亡)?

start()方法和run()方法的區(qū)別?

Runnable接口和Callable接口的區(qū)別?

volatile關(guān)鍵字的作用?

Java中如何獲取到線程dump文件?

線程和進(jìn)程有什么區(qū)別?

線程實(shí)現(xiàn)的方式有幾種(四種)?

高并發(fā)、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù)怎樣使用線程池?并發(fā)不高、任務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)怎樣使用線程池?并發(fā)高、業(yè)務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)怎樣使用線程池?

如果你提交任務(wù)時(shí),線程池隊(duì)列已滿,這時(shí)會(huì)發(fā)生什么?

鎖的等級(jí):方法鎖、對(duì)象鎖、類(lèi)鎖?

如果同步塊內(nèi)的線程拋出異常會(huì)發(fā)生什么?

并發(fā)編程(concurrency)并行編程(parallellism)有什么區(qū)別?

如何保證多線程下 i++ 結(jié)果正確?

一個(gè)線程如果出現(xiàn)了運(yùn)行時(shí)異常會(huì)怎么樣?

如何在兩個(gè)線程之間共享數(shù)據(jù)?

生產(chǎn)者消費(fèi)者模型的作用是什么?

怎么喚醒一個(gè)阻塞的線程?

Java中用到的線程調(diào)度算法是什么

單例模式的線程安全性?

線程類(lèi)的構(gòu)造方法、靜態(tài)塊是被哪個(gè)線程調(diào)用的?

同步方法和同步塊,哪個(gè)是更好的選擇?

如何檢測(cè)死鎖?怎么預(yù)防死鎖?

【前25題】想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)

前25題 想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)

CyclicBarrier和CountDownLatch的用法及區(qū)別?

CyclicBarrier和CountDownLatch 都位于java.util.concurrent 這個(gè)包下

CountDownLatch CyclicBarrier
減計(jì)數(shù)方式 加計(jì)數(shù)方式
計(jì)算為0時(shí)釋放所有等待的線程 計(jì)數(shù)達(dá)到指定值時(shí)釋放所有等待線程
計(jì)數(shù)為0時(shí),無(wú)法重置 計(jì)數(shù)達(dá)到指定值時(shí),計(jì)數(shù)置為0重新開(kāi)始
調(diào)用countDown()方法計(jì)數(shù)減一,調(diào)用await()方法只進(jìn)行阻塞,對(duì)計(jì)數(shù)沒(méi)任何影響 調(diào)用await()方法計(jì)數(shù)加1,若加1后的值不等于構(gòu)造方法的值,則線程阻塞
不可重復(fù)利用 可重復(fù)利用
一、CountDownLatch用法

CountDownLatch類(lèi)只提供了一個(gè)構(gòu)造器:

public CountDownLatch(int count) {  };  //參數(shù)count為計(jì)數(shù)值  

然后下面這3個(gè)方法是CountDownLatch類(lèi)中最重要的方法:

public void await() throws InterruptedException { };   //調(diào)用await()方法的線程會(huì)被掛起,它會(huì)等待直到count值為0才繼續(xù)執(zhí)行  
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  //和await()類(lèi)似,只不過(guò)等待一定的時(shí)間后count值還沒(méi)變?yōu)?的話就會(huì)繼續(xù)執(zhí)行  
public void countDown() { };  //將count值減1  

CountDownLatch, 一個(gè)同步輔助類(lèi),在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個(gè)或多個(gè)線程一直等待。

下面舉個(gè)例子說(shuō)明:

package main.java.CountDownLatch;  
  
import java.util.concurrent.CountDownLatch;  
  
/** 
 * PROJECT_NAME:downLoad 
 * Author:lucaifang 
 * Date:2016/3/18 
 */  
public class countDownlatchTest {  
    public static void main(String[] args) throws InterruptedException {  
        CountDownLatch countDownLatch = new CountDownLatch(5);  
        for(int i=0;i<5;i++){  
            new Thread(new readNum(i,countDownLatch)).start();  
        }  
        countDownLatch.await();  
        System.out.println("線程執(zhí)行結(jié)束。。。。");  
    }  
  
    static class readNum  implements Runnable{  
        private int id;  
        private CountDownLatch latch;  
        public readNum(int id,CountDownLatch latch){  
            this.id = id;  
            this.latch = latch;  
        }  
        @Override  
        public void run() {  
            synchronized (this){  
                System.out.println("id:"+id);  
                latch.countDown();  
                System.out.println("線程組任務(wù)"+id+"結(jié)束,其他任務(wù)繼續(xù)");  
            }  
        }  
    }  
}  

輸出結(jié)果:

id:1
線程組任務(wù)1結(jié)束,其他任務(wù)繼續(xù)
id:0
線程組任務(wù)0結(jié)束,其他任務(wù)繼續(xù)
id:2
線程組任務(wù)2結(jié)束,其他任務(wù)繼續(xù)
id:3
線程組任務(wù)3結(jié)束,其他任務(wù)繼續(xù)
id:4
線程組任務(wù)4結(jié)束,其他任務(wù)繼續(xù)
線程執(zhí)行結(jié)束。。。。

線程在countDown()之后,會(huì)繼續(xù)執(zhí)行自己的任務(wù)

二、CyclicBarrier用法

CyclicBarrier會(huì)在所有線程任務(wù)結(jié)束之后,才會(huì)進(jìn)行后續(xù)任務(wù),具體可以看下面例子。

CyclicBarrier提供2個(gè)構(gòu)造器:

public CyclicBarrier(int parties, Runnable barrierAction) {  
}  
   
public CyclicBarrier(int parties) {  
}  

參數(shù)parties指讓多少個(gè)線程或者任務(wù)等待至barrier狀態(tài);參數(shù)barrierAction為當(dāng)這些線程都達(dá)到barrier狀態(tài)時(shí)會(huì)執(zhí)行的內(nèi)容。
CyclicBarrier中最重要的方法就是await方法

//掛起當(dāng)前線程,直至所有線程都到達(dá)barrier狀態(tài)再同時(shí)執(zhí)行后續(xù)任務(wù);  
public int await() throws InterruptedException, BrokenBarrierException { };

//讓這些線程等待至一定的時(shí)間,如果還有線程沒(méi)有到達(dá)barrier狀態(tài)就直接讓到達(dá)barrier的線程執(zhí)行后續(xù)任務(wù)  
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };

舉例說(shuō)明

package main.java.countOff;  
  
import java.util.concurrent.CyclicBarrier;  
  
/** 
 * PROJECT_NAME:downLoad 
 * Author:lucaifang 
 * Date:2016/3/18 
 */  
public class cyclicBarrierTest {  
    public static void main(String[] args) throws InterruptedException {  
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {  
            @Override  
            public void run() {  
                System.out.println("線程組執(zhí)行結(jié)束");  
            }  
        });  
        for (int i = 0; i < 5; i++) {  
            new Thread(new readNum(i,cyclicBarrier)).start();  
        }  
        //CyclicBarrier 可以重復(fù)利用,  
        // 這個(gè)是CountDownLatch做不到的  
//        for (int i = 11; i < 16; i++) {  
//            new Thread(new readNum(i,cyclicBarrier)).start();  
//        }  
    }  
    static class readNum  implements Runnable{  
        private int id;  
        private CyclicBarrier cyc;  
        public readNum(int id,CyclicBarrier cyc){  
            this.id = id;  
            this.cyc = cyc;  
        }  
        @Override  
        public void run() {  
            synchronized (this){  
                System.out.println("id:"+id);  
                try {  
                    cyc.await();  
                    System.out.println("線程組任務(wù)" + id + "結(jié)束,其他任務(wù)繼續(xù)");  
                } catch (Exception e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
} 

輸出結(jié)果:

id:1
id:2
id:4
id:0
id:3
線程組執(zhí)行結(jié)束
線程組任務(wù)3結(jié)束,其他任務(wù)繼續(xù)
線程組任務(wù)1結(jié)束,其他任務(wù)繼續(xù)
線程組任務(wù)4結(jié)束,其他任務(wù)繼續(xù)
線程組任務(wù)0結(jié)束,其他任務(wù)繼續(xù)
線程組任務(wù)2結(jié)束,其他任務(wù)繼續(xù)

http://blog.csdn.net/tolcf/article/details/50925145

LockSupport工具?

1、LockSupport基本介紹與基本使用

LockSupport是JDK中比較底層的類(lèi),用來(lái)創(chuàng)建鎖和其他同步工具類(lèi)的基本線程阻塞。java鎖和同步器框架的核心 AQS: AbstractQueuedSynchronizer,就是通過(guò)調(diào)用 LockSupport .park()和 LockSupport .unpark()實(shí)現(xiàn)線程的阻塞和喚醒 的。

LockSupport 很類(lèi)似于二元信號(hào)量(只有1個(gè)許可證可供使用),如果這個(gè)許可還沒(méi)有被占用,當(dāng)前線程獲取許可并繼 續(xù) 執(zhí)行;如果許可已經(jīng)被占用,當(dāng)前線 程阻塞,等待獲取許可。

全部操作:

park()/park(Object)

等待通行準(zhǔn)許。

parkNanos(long)/parkNanos(Object, long)

在指定運(yùn)行時(shí)間(即相對(duì)時(shí)間)內(nèi),等待通行準(zhǔn)許。

parkUntil(long)/parkUntil(Object, long)

在指定到期時(shí)間(即絕對(duì)時(shí)間)內(nèi),等待通行準(zhǔn)許。

unpark(Thread)

發(fā)放通行準(zhǔn)許或提前發(fā)放。(注:不管提前發(fā)放多少次,只用于一次性使用。)

getBlocker(Thread)

進(jìn)入等待通行準(zhǔn)許時(shí),所提供的對(duì)象。

主要用途:

當(dāng)前線程需要喚醒另一個(gè)線程,但是只確定它會(huì)進(jìn)入阻塞,但不確定它是否已經(jīng)進(jìn)入阻塞,因此不管是否已經(jīng)進(jìn)入阻塞,還是準(zhǔn)備進(jìn)入阻塞,都將發(fā)放一個(gè)通行準(zhǔn)許

正確用法:

把LockSupport視為一個(gè)sleep()來(lái)用,只是sleep()是定時(shí)喚醒,LockSupport既可以定時(shí)喚醒,也可以由其它線程喚醒。

public static void main(String[] args)
{
     LockSupport.park();
     System.out.println("block.");
}

運(yùn)行該代碼,可以發(fā)現(xiàn)主線程一直處于阻塞狀態(tài)。因?yàn)?許可默認(rèn)是被占用的 ,調(diào)用park()時(shí)獲取不到許可,所以進(jìn)入阻塞狀態(tài)。

如下代碼:先釋放許可,再獲取許可,主線程能夠正常終止。LockSupport許可的獲取和釋放,一般來(lái)說(shuō)是對(duì)應(yīng)的,如果多次unpark,只有一次park也不會(huì)出現(xiàn)什么問(wèn)題,結(jié)果是許可處于可用狀態(tài)。

public static void main(String[] args)
{
     Thread thread = Thread.currentThread();
     LockSupport.unpark(thread);//釋放許可
     LockSupport.park();// 獲取許可
     System.out.println("b");
}

LockSupport是不可重入 的,如果一個(gè)線程連續(xù)2次調(diào)用 LockSupport .park(),那么該線程一定會(huì)一直阻塞下去。

public static void main(String[] args) throws Exception
{
  Thread thread = Thread.currentThread();
  
  LockSupport.unpark(thread);
  
  System.out.println("a");
  LockSupport.park();
  System.out.println("b");
  LockSupport.park();
  System.out.println("c");
}

這段代碼打印出a和b,不會(huì)打印c,因?yàn)榈诙握{(diào)用park的時(shí)候,線程無(wú)法獲取許可出現(xiàn)死鎖。

LockSupport基本介紹與基本使用

https://www.cnblogs.com/hvicen/p/6217303.html

LockSupport基本介紹與基本使用

http://www.tianshouzhi.com/api/tutorials/mutithread/303

Condition接口及其實(shí)現(xiàn)原理?

在java.util.concurrent包中,有兩個(gè)很特殊的工具類(lèi),Condition和ReentrantLock,使用過(guò)的人都知道,ReentrantLock(重入鎖)是jdk的concurrent包提供的一種獨(dú)占鎖的實(shí)現(xiàn)

我們知道在線程的同步時(shí)可以使一個(gè)線程阻塞而等待一個(gè)信號(hào),同時(shí)放棄鎖使其他線程可以能競(jìng)爭(zhēng)到鎖

在synchronized中我們可以使用Object的wait()和notify方法實(shí)現(xiàn)這種等待和喚醒

但是在Lock中怎么實(shí)現(xiàn)這種wait和notify呢

答案是Condition,學(xué)習(xí)Condition主要是為了方便以后學(xué)習(xí)blockqueue和concurrenthashmap的源碼,同時(shí)也進(jìn)一步理解ReentrantLock。

ReentrantLock和Condition的使用方式通常是這樣的:

public static void main(String[] args) {
    final ReentrantLock reentrantLock = new ReentrantLock();
    final Condition condition = reentrantLock.newCondition();
 
    Thread thread = new Thread((Runnable) () -> {
            try {
                reentrantLock.lock();
                System.out.println("我要等一個(gè)新信號(hào)" + this);
                condition.wait();
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("拿到一個(gè)信號(hào)!!" + this);
            reentrantLock.unlock();
    }, "waitThread1");
 
    thread.start();
     
    Thread thread1 = new Thread((Runnable) () -> {
            reentrantLock.lock();
            System.out.println("我拿到鎖了");
            try {
                Thread.sleep(3000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            condition.signalAll();
            System.out.println("我發(fā)了一個(gè)信號(hào)??!");
            reentrantLock.unlock();
    }, "signalThread");
     
    thread1.start();
}

運(yùn)行后,結(jié)果如下:

我要等一個(gè)新信號(hào)lock.ReentrantLockTest$1@a62fc3
我拿到鎖了
我發(fā)了一個(gè)信號(hào)??!
拿到一個(gè)信號(hào)!!

可以看到

Condition的執(zhí)行方式,是當(dāng)在線程1中調(diào)用await方法后,線程1將釋放鎖,并且將自己沉睡,等待喚醒,

線程2獲取到鎖后,開(kāi)始做事,完畢后,調(diào)用Condition的signal方法,喚醒線程1,線程1恢復(fù)執(zhí)行。

以上說(shuō)明Condition是一個(gè)多線程間協(xié)調(diào)通信的工具類(lèi),使得某個(gè),或者某些線程一起等待某個(gè)條件(Condition),只有當(dāng)該條件具備( signal 或者 signalAll方法被帶調(diào)用)時(shí) ,這些等待線程才會(huì)被喚醒,從而重新?tīng)?zhēng)奪鎖。

Condition自己也維護(hù)了一個(gè)隊(duì)列,該隊(duì)列的作用是維護(hù)一個(gè)等待signal信號(hào)的隊(duì)列,兩個(gè)隊(duì)列的作用是不同,事實(shí)上,每個(gè)線程也僅僅會(huì)同時(shí)存在以上兩個(gè)隊(duì)列中的一個(gè),流程是這樣的

線程1調(diào)用reentrantLock.lock時(shí),線程被加入到AQS的等待隊(duì)列中。

線程1調(diào)用await方法被調(diào)用時(shí),該線程從AQS中移除,對(duì)應(yīng)操作是鎖的釋放。

接著馬上被加入到Condition的等待隊(duì)列中,以為著該線程需要signal信號(hào)。

線程2,因?yàn)榫€程1釋放鎖的關(guān)系,被喚醒,并判斷可以獲取鎖,于是線程2獲取鎖,并被加入到AQS的等待隊(duì)列中。

線程2調(diào)用signal方法,這個(gè)時(shí)候Condition的等待隊(duì)列中只有線程1一個(gè)節(jié)點(diǎn),于是它被取出來(lái),并被加入到AQS的等待隊(duì)列中。 注意,這個(gè)時(shí)候,線程1 并沒(méi)有被喚醒。

signal方法執(zhí)行完畢,線程2調(diào)用reentrantLock.unLock()方法,釋放鎖。這個(gè)時(shí)候因?yàn)锳QS中只有線程1,于是,AQS釋放鎖后按從頭到尾的順序喚醒線程時(shí),線程1被喚醒,于是線程1回復(fù)執(zhí)行。

直到釋放所整個(gè)過(guò)程執(zhí)行完畢。

可以看到,整個(gè)協(xié)作過(guò)程是靠結(jié)點(diǎn)在AQS的等待隊(duì)列和Condition的等待隊(duì)列中來(lái)回移動(dòng)實(shí)現(xiàn)的,Condition作為一個(gè)條件類(lèi),很好的自己維護(hù)了一個(gè)等待信號(hào)的隊(duì)列,并在適時(shí)的時(shí)候?qū)⒔Y(jié)點(diǎn)加入到AQS的等待隊(duì)列中來(lái)實(shí)現(xiàn)的喚醒操作。

怎么理解Condition

http://www.importnew.com/9281.html

深入理解Condition

https://www.jianshu.com/p/6b5aa7b7684c

Fork/Join框架的理解? Fork/Join是什么

Oracle的官方給出的定義是:Fork/Join框架是一個(gè)實(shí)現(xiàn)了ExecutorService接口的多線程處理器。它可以把一個(gè)大的任務(wù)劃分為若干個(gè)小的任務(wù)并發(fā)執(zhí)行,充分利用可用的資源,進(jìn)而提高應(yīng)用的執(zhí)行效率。

我們?cè)偻ㄟ^(guò)Fork和Join這兩個(gè)單詞來(lái)理解下Fork/Join框架,F(xiàn)ork就是把一個(gè)大任務(wù)切分為若干子任務(wù)并行的執(zhí)行,Join就是合并這些子任務(wù)的執(zhí)行結(jié)果,最后得到這個(gè)大任務(wù)的結(jié)果。

比如計(jì)算1+2+。。+10000,可以分割成10個(gè)子任務(wù),每個(gè)子任務(wù)分別對(duì)1000個(gè)數(shù)進(jìn)行求和,最終匯總這10個(gè)子任務(wù)的結(jié)果。

工作竊取算法

工作竊取算法是指線程從其他任務(wù)隊(duì)列中竊取任務(wù)執(zhí)行(可能你會(huì)很詫異,這個(gè)算法有什么用。待會(huì)你就知道了)??紤]下面這種場(chǎng)景:有一個(gè)很大的計(jì)算任務(wù),為了減少線程的競(jìng)爭(zhēng),會(huì)將這些大任務(wù)切分為小任務(wù)并分在不同的隊(duì)列等待執(zhí)行,然后為每個(gè)任務(wù)隊(duì)列創(chuàng)建一個(gè)線程執(zhí)行隊(duì)列的任務(wù)。那么問(wèn)題來(lái)了,有的線程可能很快就執(zhí)行完了,而其他線程還有任務(wù)沒(méi)執(zhí)行完,執(zhí)行完的線程與其空閑下來(lái)不如幫助其他線程執(zhí)行任務(wù),這樣也能加快執(zhí)行進(jìn)程。所以,執(zhí)行完的空閑線程從其他隊(duì)列的尾部竊取任務(wù)執(zhí)行,而被竊取任務(wù)的線程則從隊(duì)列的頭部取任務(wù)執(zhí)行(這里使用了雙端隊(duì)列,既不影響被竊取任務(wù)的執(zhí)行過(guò)程又能加快執(zhí)行進(jìn)度)。

從以上的介紹中,能夠發(fā)現(xiàn)工作竊取算法的優(yōu)點(diǎn)是充分利用線程提高并行執(zhí)行的進(jìn)度。當(dāng)然缺點(diǎn)是在某些情況下仍然存在競(jìng)爭(zhēng),比如雙端隊(duì)列只有任務(wù)需要執(zhí)行的時(shí)候

使用Fork/Join框架分為兩步:

分割任務(wù):首先需要?jiǎng)?chuàng)建一個(gè)ForkJoin任務(wù),執(zhí)行該類(lèi)的fork方法可以對(duì)任務(wù)不斷切割,直到分割的子任務(wù)足夠小

合并任務(wù)執(zhí)行結(jié)果:子任務(wù)執(zhí)行的結(jié)果同一放在一個(gè)隊(duì)列中,通過(guò)啟動(dòng)一個(gè)線程從隊(duì)列中取執(zhí)行結(jié)果。

Fork/Join實(shí)現(xiàn)了ExecutorService,所以它的任務(wù)也需要放在線程池中執(zhí)行。它的不同在于它使用了工作竊取算法,空閑的線程可以從滿負(fù)荷的線程中竊取任務(wù)來(lái)幫忙執(zhí)行。

shitong

下面是計(jì)算1+2+3+4為例演示如何使用使用Fork/Join框架:

package com.rhwayfun.concurrency.r0406;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

/**
 * Created by rhwayfun on 16-4-6.
 */
public class CountTask extends RecursiveTask{

    //閾值
    private static final int THRESHOLD = 2;
    //起始值
    private int start;
    //結(jié)束值
    private int end;

    public CountTask(int start, int end) {
        this.start = start;
        this.end = end;
    }


    @Override
    protected Integer compute() {
        boolean compute = (end - start) <= THRESHOLD;
        int res = 0;
        if (compute){
            for (int i = start; i <= end; i++){
                res += i;
            }
        }else {
            //如果長(zhǎng)度大于閾值,則分割為小任務(wù)
            int mid = (start + end) / 2;
            CountTask task1 = new CountTask(start,mid);
            CountTask task2 = new CountTask(mid + 1, end);
            //計(jì)算小任務(wù)的值
            task1.fork();
            task2.fork();
            //得到兩個(gè)小任務(wù)的值
            int task1Res = task1.join();
            int task2Res = task2.join();
            res = task1Res + task2Res;
        }
        return res;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ForkJoinPool pool = new ForkJoinPool();
        CountTask task = new CountTask(1,5);
        ForkJoinTask submit = pool.submit(task);
        System.out.println("Final result:" + submit.get());
    }
}

代碼執(zhí)行結(jié)果為:

15

代碼中使用了FokJoinTask,其與一般任務(wù)的區(qū)別在于它需要實(shí)現(xiàn)compute方法,在方法需要判斷任務(wù)是否在閾值區(qū)間內(nèi),如果不是則需要把任務(wù)切分到足夠小,直到能夠進(jìn)行計(jì)算

每個(gè)被切分的子任務(wù)又會(huì)重新進(jìn)入compute方法,再繼續(xù)判斷是否需要繼續(xù)切分,如果不需要?jiǎng)t直接得到子任務(wù)執(zhí)行的結(jié)果,如果需要的話則繼續(xù)切分,如此循環(huán),直到調(diào)用join方法得到最終的結(jié)果。
**
可以發(fā)現(xiàn)Fork/Join框架的需要把提交給ForkJoinPool,F(xiàn)orkJoinPool由ForkJoinTask數(shù)組和ForkJoinWorkerThread數(shù)組組成,前者負(fù)責(zé)將存放程序提交給ForkJoinPool的任務(wù),后者則負(fù)責(zé)執(zhí)行這些任務(wù)。關(guān)鍵在于在于fork方法與join方法**

Java并發(fā)編程系列之二十:Fork/Join框架

http://blog.csdn.net/u011116672/article/details/51073683

wait()和sleep()的區(qū)別?

sleep()

方法是線程類(lèi)(Thread)的靜態(tài)方法,讓調(diào)用線程進(jìn)入睡眠狀態(tài),讓出執(zhí)行機(jī)會(huì)給其他線程,等到休眠時(shí)間結(jié)束后,線程進(jìn)入就緒狀態(tài)和其他線程一起競(jìng)爭(zhēng)cpu的執(zhí)行時(shí)間。

因?yàn)閟leep() 是static靜態(tài)的方法,他不能改變對(duì)象的機(jī)鎖,當(dāng)一個(gè)synchronized塊中調(diào)用了sleep() 方法,線程雖然進(jìn)入休眠,但是對(duì)象的機(jī)鎖沒(méi)有被釋放,其他線程依然無(wú)法訪問(wèn)這個(gè)對(duì)象。

wait()

wait()是Object類(lèi)的方法,當(dāng)一個(gè)線程執(zhí)行到wait方法時(shí),它就進(jìn)入到一個(gè)和該對(duì)象相關(guān)的等待池,同時(shí)釋放對(duì)象的機(jī)鎖,使得其他線程能夠訪問(wèn),可以通過(guò)notify,notifyAll方法來(lái)喚醒等待的線程

線程的五個(gè)狀態(tài)(五種狀態(tài),創(chuàng)建、就緒、運(yùn)行、阻塞和死亡)?

線程通常都有五種狀態(tài),創(chuàng)建、就緒、運(yùn)行、阻塞和死亡。

第一是創(chuàng)建狀態(tài)。在生成線程對(duì)象,并沒(méi)有調(diào)用該對(duì)象的start方法,這是線程處于創(chuàng)建狀態(tài)。

第二是就緒狀態(tài)。當(dāng)調(diào)用了線程對(duì)象的start方法之后,該線程就進(jìn)入了就緒狀態(tài),但是此時(shí)線程調(diào)度程序還沒(méi)有把該線程設(shè)置為當(dāng)前線程,此時(shí)處于就緒狀態(tài)。在線程運(yùn)行之后,從等待或者睡眠中回來(lái)之后,也會(huì)處于就緒狀態(tài)。

第三是運(yùn)行狀態(tài)。線程調(diào)度程序?qū)⑻幱诰途w狀態(tài)的線程設(shè)置為當(dāng)前線程,此時(shí)線程就進(jìn)入了運(yùn)行狀態(tài),開(kāi)始運(yùn)行run函數(shù)當(dāng)中的代碼。

第四是阻塞狀態(tài)。線程正在運(yùn)行的時(shí)候,被暫停,通常是為了等待某個(gè)時(shí)間的發(fā)生(比如說(shuō)某項(xiàng)資源就緒)之后再繼續(xù)運(yùn)行。sleep,suspend,wait等方法都可以導(dǎo)致線程阻塞。

第五是死亡狀態(tài)。如果一個(gè)線程的run方法執(zhí)行結(jié)束或者調(diào)用stop方法后,該線程就會(huì)死亡。對(duì)于已經(jīng)死亡的線程,無(wú)法再使用start方法令其進(jìn)入就緒

  

start()方法和run()方法的區(qū)別?

每個(gè)線程都是通過(guò)某個(gè)特定Thread對(duì)象所對(duì)應(yīng)的方法run()來(lái)完成其操作的,方法run()稱(chēng)為線程體。通過(guò)調(diào)用Thread類(lèi)的start()方法來(lái)啟動(dòng)一個(gè)線程。

start()方法來(lái)啟動(dòng)一個(gè)線程,真正實(shí)現(xiàn)了多線程運(yùn)行。這時(shí)無(wú)需等待run方法體代碼執(zhí)行完畢,可以直接繼續(xù)執(zhí)行下面的代碼;
這時(shí)此線程是處于就緒狀態(tài), 并沒(méi)有運(yùn)行。 然后通過(guò)此Thread類(lèi)調(diào)用方法run()來(lái)完成其運(yùn)行狀態(tài), 這里方法run()稱(chēng)為線程體,它包含了要執(zhí)行的這個(gè)線程的內(nèi)容, Run方法運(yùn)行結(jié)束, 此線程終止。然后CPU再調(diào)度其它線程。

run()方法是在本線程里的,只是線程里的一個(gè)函數(shù),而不是多線程的。
如果直接調(diào)用run(),其實(shí)就相當(dāng)于是調(diào)用了一個(gè)普通函數(shù)而已,直接待用run()方法必須等待run()方法執(zhí)行完畢才能執(zhí)行下面的代碼,所以執(zhí)行路徑還是只有一條,根本就沒(méi)有線程的特征,所以在多線程執(zhí)行時(shí)要使用start()方法而不是run()方法。

Runnable接口和Callable接口的區(qū)別?

有點(diǎn)深的問(wèn)題了,也看出一個(gè)Java程序員學(xué)習(xí)知識(shí)的廣度。

Runnable接口中的run()方法的返回值是void,它做的事情只是純粹地去執(zhí)行run()方法中的代碼而已;

Callable接口中的call()方法是有返回值的,是一個(gè)泛型,和Future、FutureTask配合可以用來(lái)獲取異步執(zhí)行的結(jié)果。

這其實(shí)是很有用的一個(gè)特性,因?yàn)槎嗑€程相比單線程更難、更復(fù)雜的一個(gè)重要原因就是因?yàn)槎嗑€程充滿著未知性,某條線程是否執(zhí)行了?某條線程執(zhí)行了多久?某條線程執(zhí)行的時(shí)候我們期望的數(shù)據(jù)是否已經(jīng)賦值完畢?無(wú)法得知,我們能做的只是等待這條多線程的任務(wù)執(zhí)行完畢而已。而Callable+Future/FutureTask卻可以獲取多線程運(yùn)行的結(jié)果,可以在等待時(shí)間太長(zhǎng)沒(méi)獲取到需要的數(shù)據(jù)的情況下取消該線程的任務(wù),真的是非常有用。

volatile關(guān)鍵字的作用?

volatile關(guān)鍵字的作用主要有兩個(gè):

(1)多線程主要圍繞可見(jiàn)性和原子性?xún)蓚€(gè)特性而展開(kāi),使用volatile關(guān)鍵字修飾的變量,保證了其在多線程之間的可見(jiàn)性,即每次讀取到volatile變量,一定是最新的數(shù)據(jù)

(2)代碼底層執(zhí)行不像我們看到的高級(jí)語(yǔ)言—-Java程序這么簡(jiǎn)單,它的執(zhí)行是Java代碼–>字節(jié)碼–>根據(jù)字節(jié)碼執(zhí)行對(duì)應(yīng)的C/C++代碼–>C/C++代碼被編譯成匯編語(yǔ)言–>和硬件電路交互,現(xiàn)實(shí)中,為了獲取更好的性能JVM可能會(huì)對(duì)指令進(jìn)行重排序,多線程下可能會(huì)出現(xiàn)一些意想不到的問(wèn)題。使用volatile則會(huì)對(duì)禁止語(yǔ)義重排序,當(dāng)然這也一定程度上降低了代碼執(zhí)行效率

從實(shí)踐角度而言,volatile的一個(gè)重要作用就是和CAS結(jié)合,保證了原子性,詳細(xì)的可以參見(jiàn)java.util.concurrent.atomic包下的類(lèi),比如AtomicInteger。

Java中如何獲取到線程dump文件?

死循環(huán)、死鎖、阻塞、頁(yè)面打開(kāi)慢等問(wèn)題,打線程dump是最好的解決問(wèn)題的途徑。所謂線程dump也就是線程堆棧,獲取到線程堆棧有兩步:

(1)獲取到線程的pid,可以通過(guò)使用jps命令,在Linux環(huán)境下還可以使用ps -ef | grep java

(2)打印線程堆棧,可以通過(guò)使用jstack pid命令,在Linux環(huán)境下還可以使用kill -3 pid

另外提一點(diǎn),Thread類(lèi)提供了一個(gè)getStackTrace()方法也可以用于獲取線程堆棧。這是一個(gè)實(shí)例方法,因此此方法是和具體線程實(shí)例綁定的,每次獲取獲取到的是具體某個(gè)線程當(dāng)前運(yùn)行的堆棧,

虛擬機(jī)性能監(jiān)控與故障處理工具 詳解

http://www.ymq.io/2017/08/01/jvm-4/

線程和進(jìn)程有什么區(qū)別?

進(jìn)程是系統(tǒng)進(jìn)行資源分配的基本單位,有獨(dú)立的內(nèi)存地址空間

線程是CPU獨(dú)立運(yùn)行和獨(dú)立調(diào)度的基本單位,沒(méi)有多帶帶地址空間,有獨(dú)立的棧,局部變量,寄存器, 程序計(jì)數(shù)器等。

創(chuàng)建進(jìn)程的開(kāi)銷(xiāo)大,包括創(chuàng)建虛擬地址空間等需要大量系統(tǒng)資源

創(chuàng)建線程開(kāi)銷(xiāo)小,基本上只有一個(gè)內(nèi)核對(duì)象和一個(gè)堆棧。

一個(gè)進(jìn)程無(wú)法直接訪問(wèn)另一個(gè)進(jìn)程的資源;同一進(jìn)程內(nèi)的多個(gè)線程共享進(jìn)程的資源。

進(jìn)程切換開(kāi)銷(xiāo)大,線程切換開(kāi)銷(xiāo)?。贿M(jìn)程間通信開(kāi)銷(xiāo)大,線程間通信開(kāi)銷(xiāo)小。

線程屬于進(jìn)程,不能獨(dú)立執(zhí)行。每個(gè)進(jìn)程至少要有一個(gè)線程,成為主線程

線程實(shí)現(xiàn)的方式有幾種(四種)?

繼承Thread類(lèi),重寫(xiě)run方法

實(shí)現(xiàn)Runnable接口,重寫(xiě)run方法,實(shí)現(xiàn)Runnable接口的實(shí)現(xiàn)類(lèi)的實(shí)例對(duì)象作為T(mén)hread構(gòu)造函數(shù)的target

實(shí)現(xiàn)Callable接口通過(guò)FutureTask包裝器來(lái)創(chuàng)建Thread線程

通過(guò)線程池創(chuàng)建線程

前面兩種可以歸結(jié)為一類(lèi):無(wú)返回值,原因很簡(jiǎn)單,通過(guò)重寫(xiě)run方法,run方式的返回值是void,所以沒(méi)有辦法返回結(jié)果

后面兩種可以歸結(jié)成一類(lèi):有返回值,通過(guò)Callable接口,就要實(shí)現(xiàn)call方法,這個(gè)方法的返回值是Object,所以返回的結(jié)果可以放在Object對(duì)象中

線程實(shí)現(xiàn)方式3:通過(guò)Callable和FutureTask創(chuàng)建線程

創(chuàng)建Callable接口的實(shí)現(xiàn)類(lèi) ,并實(shí)現(xiàn)Call方法

創(chuàng)建Callable實(shí)現(xiàn)類(lèi)的實(shí)現(xiàn),使用FutureTask類(lèi)包裝Callable對(duì)象,該FutureTask對(duì)象封裝了Callable對(duì)象的Call方法的返回值

使用FutureTask對(duì)象作為T(mén)hread對(duì)象的target創(chuàng)建并啟動(dòng)線程

調(diào)用FutureTask對(duì)象的get()來(lái)獲取子線程執(zhí)行結(jié)束的返回值

public class ThreadDemo03 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Callable oneCallable = new Tickets();
        FutureTask oneTask = new FutureTask(oneCallable);

        Thread t = new Thread(oneTask);

        System.out.println(Thread.currentThread().getName());

        t.start();

    }

}

class Tickets implements Callable{

    //重寫(xiě)call方法
    @Override
    public Object call() throws Exception {
        // TODO Auto-generated method stub
        System.out.println(Thread.currentThread().getName()+"-->我是通過(guò)實(shí)現(xiàn)Callable接口通過(guò)FutureTask包裝器來(lái)實(shí)現(xiàn)的線程");
        return null;
    }   
}

程序運(yùn)行結(jié)果:

main
Thread-0–>我是通過(guò)實(shí)現(xiàn)Callable接口通過(guò)FutureTask包裝器來(lái)實(shí)現(xiàn)的線程

線程實(shí)現(xiàn)方式4:通過(guò)線程池創(chuàng)建線程
public class ThreadDemo05{

    private static int POOL_NUM = 10;     //線程池?cái)?shù)量

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        ExecutorService executorService = Executors.newFixedThreadPool(5);  
        for(int i = 0; i

程序運(yùn)行結(jié)果:

通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-3 
通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-4 
通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-1 
通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-5 
通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-2 
通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-5 
通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-1 
通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-4 
通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-3 
通過(guò)線程池方式創(chuàng)建的線程:pool-1-thread-2

ExecutorService、Callable都是屬于Executor框架。返回結(jié)果的線程是在JDK1.5中引入的新特征,還有Future接口也是屬于這個(gè)框架,有了這種特征得到返回值就很方便了。
通過(guò)分析可以知道,他同樣也是實(shí)現(xiàn)了Callable接口,實(shí)現(xiàn)了Call方法,所以有返回值。這也就是正好符合了前面所說(shuō)的兩種分類(lèi)

執(zhí)行Callable任務(wù)后,可以獲取一個(gè)Future的對(duì)象,在該對(duì)象上調(diào)用get就可以獲取到Callable任務(wù)返回的Object了。get方法是阻塞的,即:線程無(wú)返回結(jié)果,get方法會(huì)一直等待。

再介紹Executors類(lèi):

newCachedThreadPool創(chuàng)建一個(gè)可緩存線程池,如果線程池長(zhǎng)度超過(guò)處理需要,可靈活回收空閑線程,若無(wú)可回收,則新建線程。

newFixedThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線程池,可控制線程最大并發(fā)數(shù),超出的線程會(huì)在隊(duì)列中等待。

newScheduledThreadPool 創(chuàng)建一個(gè)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。

newSingleThreadExecutor 創(chuàng)建一個(gè)單線程化的線程池,它只會(huì)用唯一的工作線程來(lái)執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級(jí))執(zhí)行。

Java多線程實(shí)現(xiàn)的四種方式

http://blog.csdn.net/u011480603/article/details/75332435

高并發(fā)、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù)怎樣使用線程池?并發(fā)不高、任務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)怎樣使用線程池?并發(fā)高、業(yè)務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)怎樣使用線程池?

這是我在并發(fā)編程網(wǎng)上看到的一個(gè)問(wèn)題,把這個(gè)問(wèn)題放在最后一個(gè),希望每個(gè)人都能看到并且思考一下,因?yàn)檫@個(gè)問(wèn)題非常好、非常實(shí)際、非常專(zhuān)業(yè)。關(guān)于這個(gè)問(wèn)題,個(gè)人看法是:

(1)高并發(fā)、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù),線程池線程數(shù)可以設(shè)置為CPU核數(shù)+1,減少線程上下文的切換

(2)并發(fā)不高、任務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)要區(qū)分開(kāi)看:

a)假如是業(yè)務(wù)時(shí)間長(zhǎng)集中在IO操作上,也就是IO密集型的任務(wù),因?yàn)镮O操作并不占用CPU,所以不要讓所有的CPU閑下來(lái),可以加大線程池中的線程數(shù)目,讓CPU處理更多的業(yè)務(wù)

b)假如是業(yè)務(wù)時(shí)間長(zhǎng)集中在計(jì)算操作上,也就是計(jì)算密集型任務(wù),這個(gè)就沒(méi)辦法了,和(1)一樣吧,線程池中的線程數(shù)設(shè)置得少一些,減少線程上下文的切換

(3)并發(fā)高、業(yè)務(wù)執(zhí)行時(shí)間長(zhǎng),解決這種類(lèi)型任務(wù)的關(guān)鍵不在于線程池而在于整體架構(gòu)的設(shè)計(jì),看看這些業(yè)務(wù)里面某些數(shù)據(jù)是否能做緩存是第一步,增加服務(wù)器是第二步,至于線程池的設(shè)置,設(shè)置參考(2)。最后,業(yè)務(wù)執(zhí)行時(shí)間長(zhǎng)的問(wèn)題,也可能需要分析一下,看看能不能使用中間件對(duì)任務(wù)進(jìn)行拆分和解耦。

如果你提交任務(wù)時(shí),線程池隊(duì)列已滿,這時(shí)會(huì)發(fā)生什么?

如果你使用的LinkedBlockingQueue,也就是無(wú)界隊(duì)列的話,沒(méi)關(guān)系,繼續(xù)添加任務(wù)到阻塞隊(duì)列中等待執(zhí)行,因?yàn)長(zhǎng)inkedBlockingQueue可以近乎認(rèn)為是一個(gè)無(wú)窮大的隊(duì)列,可以無(wú)限存放任務(wù);如果你使用的是有界隊(duì)列比方說(shuō)ArrayBlockingQueue的話,任務(wù)首先會(huì)被添加到ArrayBlockingQueue中,ArrayBlockingQueue滿了,則會(huì)使用拒絕策略RejectedExecutionHandler處理滿了的任務(wù),默認(rèn)是AbortPolicy。

鎖的等級(jí):方法鎖、對(duì)象鎖、類(lèi)鎖?

方法鎖(synchronized修飾方法時(shí))

通過(guò)在方法聲明中加入 synchronized關(guān)鍵字來(lái)聲明 synchronized 方法。

synchronized 方法控制對(duì)類(lèi)成員變量的訪問(wèn):
每個(gè)類(lèi)實(shí)例對(duì)應(yīng)一把鎖,每個(gè) synchronized 方法都必須獲得調(diào)用該方法的類(lèi)實(shí)例的鎖方能執(zhí)行,否則所屬線程阻塞,方法一旦執(zhí)行,就獨(dú)占該鎖,直到從該方法返回時(shí)才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài)。這種機(jī)制確保了同一時(shí)刻對(duì)于每一個(gè)類(lèi)實(shí)例,其所有聲明為 synchronized 的成員函數(shù)中至多只有一個(gè)處于可執(zhí)行狀態(tài),從而有效避免了類(lèi)成員變量的訪問(wèn)沖突。

對(duì)象鎖(synchronized修飾方法或代碼塊)

當(dāng)一個(gè)對(duì)象中有synchronized method或synchronized block的時(shí)候調(diào)用此對(duì)象的同步方法或進(jìn)入其同步區(qū)域時(shí),就必須先獲得對(duì)象鎖。如果此對(duì)象的對(duì)象鎖已被其他調(diào)用者占用,則需要等待此鎖被釋放。(方法鎖也是對(duì)象鎖)       

java的所有對(duì)象都含有1個(gè)互斥鎖,這個(gè)鎖由JVM自動(dòng)獲取和釋放。線程進(jìn)入synchronized方法的時(shí)候獲取該對(duì)象的鎖,當(dāng)然如果已經(jīng)有線程獲取了這個(gè)對(duì)象的鎖,那么當(dāng)前線程會(huì)等待;synchronized方法正常返回或者拋異常而終止,JVM會(huì)自動(dòng)釋放對(duì)象鎖。這里也體現(xiàn)了用synchronized來(lái)加鎖的1個(gè)好處,方法拋異常的時(shí)候,鎖仍然可以由JVM來(lái)自動(dòng)釋放?!?/p>

類(lèi)鎖(synchronized 修飾靜態(tài)的方法或代碼塊)

由于一個(gè)class不論被實(shí)例化多少次,其中的靜態(tài)方法和靜態(tài)變量在內(nèi)存中都只有一份。所以,一旦一個(gè)靜態(tài)的方法被申明為synchronized。此類(lèi)所有的實(shí)例化對(duì)象在調(diào)用此方法,共用同一把鎖,我們稱(chēng)之為類(lèi)鎖。  

對(duì)象鎖是用來(lái)控制實(shí)例方法之間的同步,類(lèi)鎖是用來(lái)控制靜態(tài)方法(或靜態(tài)變量互斥體)之間的同步

如果同步塊內(nèi)的線程拋出異常會(huì)發(fā)生什么?

這個(gè)問(wèn)題坑了很多Java程序員,若你能想到鎖是否釋放這條線索來(lái)回答還有點(diǎn)希望答對(duì)。無(wú)論你的同步塊是正常還是異常退出的,里面的線程都會(huì)釋放鎖,所以對(duì)比鎖接口我更喜歡同步塊,因?yàn)樗挥梦一ㄙM(fèi)精力去釋放鎖,該功能可以在finally block里釋放鎖實(shí)現(xiàn)。

并發(fā)編程(concurrency)并行編程(parallellism)有什么區(qū)別?

并發(fā)(concurrency)和并行(parallellism)是:

解釋一:并行是指兩個(gè)或者多個(gè)事件在同一時(shí)刻發(fā)生;而并發(fā)是指兩個(gè)或多個(gè)事件在同一時(shí)間間隔發(fā)生。

解釋二:并行是在不同實(shí)體上的多個(gè)事件,并發(fā)是在同一實(shí)體上的多個(gè)事件。

解釋三:在一臺(tái)處理器上“同時(shí)”處理多個(gè)任務(wù),在多臺(tái)處理器上同時(shí)處理多個(gè)任務(wù)。如hadoop分布式集群

所以并發(fā)編程的目標(biāo)是充分的利用處理器的每一個(gè)核,以達(dá)到最高的處理性能。

如何保證多線程下 i++ 結(jié)果正確?

根據(jù)volatile特性來(lái)用1000個(gè)線程不斷的累加數(shù)字,每次累加1個(gè),到最后值確不是1000.

volatile只能保證你數(shù)據(jù)的可見(jiàn)性(獲取到的是最新的數(shù)據(jù),不能保證原子性,說(shuō)白了,volatile跟原子性沒(méi)關(guān)系

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class Counter {

    public static AtomicInteger count = new AtomicInteger();//原子操作
    public static CountDownLatch latch= new CountDownLatch(1000);//線程協(xié)作處理
    public static volatile int countNum = 0;//volatile    只能保證可見(jiàn)性,不能保證原子性
    public static int synNum = 0;//同步處理計(jì)算

    public static void inc() {

        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
        }
        countNum++;
        int c = count.addAndGet(1);
        add();
        System.out.println(Thread.currentThread().getName() + "------>" + c);
    }

    public static synchronized void add(){
        synNum++;
    }

    public static void main(String[] args) {

        //同時(shí)啟動(dòng)1000個(gè)線程,去進(jìn)行i++計(jì)算,看看實(shí)際結(jié)果

        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Counter.inc();
                    latch.countDown();
                }
            },"thread" + i).start();
        }
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());

        System.out.println("運(yùn)行結(jié)果:Counter.count=" + count.get() + ",,," + countNum + ",,," + synNum);
    }

count.get()是AtomicInteger的值;

count是用volatile修飾的變量的值;

synNum是用synchronized修飾的值;

用synchronized和AtomicInteger能保證是你想要的數(shù)據(jù),volatile并不能保證。

第一次運(yùn)行結(jié)果

main
運(yùn)行結(jié)果:Counter.count=1000,,,991,,,1000

第二次運(yùn)行結(jié)果:

main
運(yùn)行結(jié)果:Counter.count=1000,,,998,,,1000

第三次運(yùn)行結(jié)果:

main
運(yùn)行結(jié)果:Counter.count=1000,,,993,,,1000

可見(jiàn),就算用了volatile,也不能保證數(shù)據(jù)是你想要的數(shù)據(jù),volatile只能保證你數(shù)據(jù)的可見(jiàn)性(獲取到的是最新的數(shù)據(jù),不能保證原子性,說(shuō)白了,volatile跟原子性沒(méi)關(guān)系)

要保證原子性,對(duì)數(shù)據(jù)的累加,可以用AtomicInteger類(lèi);

也可以用synchronized來(lái)保證數(shù)據(jù)的一致性

一個(gè)線程如果出現(xiàn)了運(yùn)行時(shí)異常會(huì)怎么樣?

如果這個(gè)異常沒(méi)有被捕獲的話,這個(gè)線程就停止執(zhí)行了。另外重要的一點(diǎn)是:如果這個(gè)線程持有某個(gè)某個(gè)對(duì)象的監(jiān)視器,那么這個(gè)對(duì)象監(jiān)視器會(huì)被立即釋放

如何在兩個(gè)線程之間共享數(shù)據(jù)?

通過(guò)在線程之間共享對(duì)象就可以了,然后通過(guò)wait/notify/notifyAll、await/signal/signalAll進(jìn)行喚起和等待,比方說(shuō)阻塞隊(duì)列BlockingQueue就是為線程之間共享數(shù)據(jù)而設(shè)計(jì)的

生產(chǎn)者消費(fèi)者模型的作用是什么?

這個(gè)問(wèn)題很理論,但是很重要:

(1)通過(guò)平衡生產(chǎn)者的生產(chǎn)能力和消費(fèi)者的消費(fèi)能力來(lái)提升整個(gè)系統(tǒng)的運(yùn)行效率,這是生產(chǎn)者消費(fèi)者模型最重要的作用

(2)解耦,這是生產(chǎn)者消費(fèi)者模型附帶的作用,解耦意味著生產(chǎn)者和消費(fèi)者之間的聯(lián)系少,聯(lián)系越少越可以獨(dú)自發(fā)展而不需要收到相互的制約

怎么喚醒一個(gè)阻塞的線程?

如果線程是因?yàn)檎{(diào)用了wait()、sleep()或者join()方法而導(dǎo)致的阻塞,可以中斷線程,并且通過(guò)拋出InterruptedException來(lái)喚醒它;如果線程遇到了IO阻塞,無(wú)能為力,因?yàn)镮O是操作系統(tǒng)實(shí)現(xiàn)的,Java代碼并沒(méi)有辦法直接接觸到操作系統(tǒng)。

Java中用到的線程調(diào)度算法是什么?

搶占式。一個(gè)線程用完CPU之后,操作系統(tǒng)會(huì)根據(jù)線程優(yōu)先級(jí)、線程饑餓情況等數(shù)據(jù)算出一個(gè)總的優(yōu)先級(jí)并分配下一個(gè)時(shí)間片給某個(gè)線程執(zhí)行。

單例模式的線程安全性?

老生常談的問(wèn)題了,首先要說(shuō)的是單例模式的線程安全意味著:某個(gè)類(lèi)的實(shí)例在多線程環(huán)境下只會(huì)被創(chuàng)建一次出來(lái)。單例模式有很多種的寫(xiě)法,我總結(jié)一下:

(1)餓漢式單例模式的寫(xiě)法:線程安全

(2)懶漢式單例模式的寫(xiě)法:非線程安全

(3)雙檢鎖單例模式的寫(xiě)法:線程安全

線程類(lèi)的構(gòu)造方法、靜態(tài)塊是被哪個(gè)線程調(diào)用的?

這是一個(gè)非常刁鉆和狡猾的問(wèn)題。請(qǐng)記住:線程類(lèi)的構(gòu)造方法、靜態(tài)塊是被new這個(gè)線程類(lèi)所在的線程所調(diào)用的,而run方法里面的代碼才是被線程自身所調(diào)用的。

如果說(shuō)上面的說(shuō)法讓你感到困惑,那么我舉個(gè)例子,假設(shè)Thread2中new了Thread1,main函數(shù)中new了Thread2,那么:

(1)Thread2的構(gòu)造方法、靜態(tài)塊是main線程調(diào)用的,Thread2的run()方法是Thread2自己調(diào)用的

(2)Thread1的構(gòu)造方法、靜態(tài)塊是Thread2調(diào)用的,Thread1的run()方法是Thread1自己調(diào)用的

同步方法和同步塊,哪個(gè)是更好的選擇?

同步塊是更好的選擇,因?yàn)樗粫?huì)鎖住整個(gè)對(duì)象(當(dāng)然也可以讓它鎖住整個(gè)對(duì)象)。同步方法會(huì)鎖住整個(gè)對(duì)象,哪怕這個(gè)類(lèi)中有多個(gè)不相關(guān)聯(lián)的同步塊,這通常會(huì)導(dǎo)致他們停止執(zhí)行并需要等待獲得這個(gè)對(duì)象上的鎖。

public class SynObj{

public synchronized void showA(){
    System.out.println("showA..");
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public void showB(){
    synchronized (this) {
        System.out.println("showB..");
    }
}

}

如何檢測(cè)死鎖?怎么預(yù)防死鎖?

所謂死鎖:是指兩個(gè)或兩個(gè)以上的進(jìn)程在執(zhí)行過(guò)程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象,若無(wú)外力作用,它們都將無(wú)法推進(jìn)下去。此時(shí)稱(chēng)系統(tǒng)處于死鎖

通俗地講就是兩個(gè)或多個(gè)進(jìn)程被無(wú)限期地阻塞、相互等待的一種狀態(tài)

死鎖產(chǎn)生的原因?

1.因競(jìng)爭(zhēng)資源發(fā)生死鎖 現(xiàn)象:系統(tǒng)中供多個(gè)進(jìn)程共享的資源的數(shù)目不足以滿足全部進(jìn)程的需要時(shí),就會(huì)引起對(duì)諸資源的競(jìng)爭(zhēng)而發(fā)生死鎖現(xiàn)象

2.進(jìn)程推進(jìn)順序不當(dāng)發(fā)生死鎖

死鎖的四個(gè)必要條件:

互斥條件:進(jìn)程對(duì)所分配到的資源不允許其他進(jìn)程進(jìn)行訪問(wèn),若其他進(jìn)程訪問(wèn)該資源,只能等待,直至占有該資源的進(jìn)程使用完成后釋放該資源

請(qǐng)求和保持條件:進(jìn)程獲得一定的資源之后,又對(duì)其他資源發(fā)出請(qǐng)求,但是該資源可能被其他進(jìn)程占有,此事請(qǐng)求阻塞,但又對(duì)自己獲得的資源保持不放

不可剝奪條件:是指進(jìn)程已獲得的資源,在未完成使用之前,不可被剝奪,只能在使用完后自己釋放

環(huán)路等待條件:是指進(jìn)程發(fā)生死鎖后,若干進(jìn)程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系

這四個(gè)條件是死鎖的必要條件,只要系統(tǒng)發(fā)生死鎖,這些條件必然成立,而只要上述條件之
一不滿足,就不會(huì)發(fā)生死鎖。

檢測(cè)死鎖

有兩個(gè)容器,一個(gè)用于保存線程正在請(qǐng)求的鎖,一個(gè)用于保存線程已經(jīng)持有的鎖。每次加鎖之前都會(huì)做如下檢測(cè):

檢測(cè)當(dāng)前正在請(qǐng)求的鎖是否已經(jīng)被其它線程持有,如果有,則把那些線程找出來(lái)

遍歷第一步中返回的線程,檢查自己持有的鎖是否正被其中任何一個(gè)線程請(qǐng)求,如果第二步返回真,表示出現(xiàn)了死鎖

死鎖的解除與預(yù)防:

理解了死鎖的原因,尤其是產(chǎn)生死鎖的四個(gè)必要條件,就可以最大可能地避免、預(yù)防和
解除死鎖。

所以,在系統(tǒng)設(shè)計(jì)、進(jìn)程調(diào)度等方面注意如何不讓這四個(gè)必要條件成立,如何確
定資源的合理分配算法,避免進(jìn)程永久占據(jù)系統(tǒng)資源。

此外,也要防止進(jìn)程在處于等待狀態(tài)的情況下占用資源。因此,對(duì)資源的分配要給予合理的規(guī)劃。

http://blog.csdn.net/yyf_it/a...
http://blog.csdn.net/yyf_it/article/details/52412071

推薦閱讀

想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(一)

想進(jìn)大廠?50個(gè)多線程面試題,你會(huì)多少?(二)

BTA 常問(wèn)的 Java基礎(chǔ)40道常見(jiàn)面試題及詳細(xì)答案

Spring 常見(jiàn)的一些面試題整理

常用的分布式事務(wù)解決方案介紹有多少種?

什么是微服務(wù)架構(gòu)?

Dapper,大規(guī)模分布式系統(tǒng)的跟蹤系統(tǒng)

Contact

作者:鵬磊

出處:http://www.ymq.io

版權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)注明出處

Wechat:關(guān)注公眾號(hào),搜云庫(kù),專(zhuān)注于開(kāi)發(fā)技術(shù)的研究與知識(shí)分享

關(guān)注微信公眾號(hào) "搜云庫(kù)" 獲取最新文章 【福利】公眾號(hào)后臺(tái)回復(fù) “進(jìn)群” 拉你進(jìn)微信【技術(shù)分享群】 【福利】在里面你可以認(rèn)識(shí)到很多搞技術(shù)大佬,免費(fèi)提問(wèn),互相學(xué)習(xí)

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68879.html

相關(guān)文章

  • 為Java程序員金三銀四精心挑選的300余道Java面試與答案

    摘要:為程序員金三銀四精心挑選的余道面試題與答案,歡迎大家向我推薦你在面試過(guò)程中遇到的問(wèn)題我會(huì)把大家推薦的問(wèn)題添加到下面的常用面試題清單中供大家參考。 為Java程序員金三銀四精心挑選的300余道Java面試題與答案,歡迎大家向我推薦你在面試過(guò)程中遇到的問(wèn)題,我會(huì)把大家推薦的問(wèn)題添加到下面的常用面試題清單中供大家參考。 前兩天寫(xiě)的以下博客,大家比較認(rèn)可,熱度不錯(cuò),希望可以幫到準(zhǔn)備或者正在參加...

    tomorrowwu 評(píng)論0 收藏0
  • Segmentfault JAVA文章 收藏量TOP20

    摘要:前言從號(hào)開(kāi)始在寫(xiě)下第一篇文章說(shuō)是筆記還差不多,驚奇地收到有人收藏我的文章的消息,覺(jué)得有點(diǎn)開(kāi)心。突然腦子抽到想爬下里標(biāo)簽下的文章有多少,哪篇被收藏最多,哪篇被點(diǎn)贊最多。。?,F(xiàn)在和大家分享下,收藏量前的文章,被那么多人收藏應(yīng)該是篇值得看的文章。 前言 從18號(hào)開(kāi)始在sf寫(xiě)下第一篇文章(說(shuō)是筆記還差不多),驚奇地收到有人收藏我的文章的消息,覺(jué)得有點(diǎn)開(kāi)心。突然腦子抽到想爬下sf里JAVA標(biāo)簽下...

    zhaofeihao 評(píng)論0 收藏0
  • 想進(jìn)大廠?50個(gè)多線程面試,你會(huì)多少?(一)

    摘要:下面是線程相關(guān)的熱門(mén)面試題,你可以用它來(lái)好好準(zhǔn)備面試。線程安全問(wèn)題都是由全局變量及靜態(tài)變量引起的。持有自旋鎖的線程在之前應(yīng)該釋放自旋鎖以便其它線程可以獲得自旋鎖。 最近看到網(wǎng)上流傳著,各種面試經(jīng)驗(yàn)及面試題,往往都是一大堆技術(shù)題目貼上去,而沒(méi)有答案。 不管你是新程序員還是老手,你一定在面試中遇到過(guò)有關(guān)線程的問(wèn)題。Java語(yǔ)言一個(gè)重要的特點(diǎn)就是內(nèi)置了對(duì)并發(fā)的支持,讓Java大受企業(yè)和程序員...

    wow_worktile 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<