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

資訊專欄INFORMATION COLUMN

java高并發(fā)之從零到放棄(三)

FrozenMap / 2955人閱讀

摘要:前言今天講的多線程的同步控制直接進(jìn)入正題重入鎖重入鎖可以完全代替,它需要類(lèi)來(lái)實(shí)現(xiàn)下面用一個(gè)簡(jiǎn)單的例子來(lái)實(shí)現(xiàn)重入鎖以上代碼打印出來(lái)的是,可以說(shuō)明也實(shí)現(xiàn)了線程同步它相比更加靈活,因?yàn)橹厝腈i實(shí)現(xiàn)了用戶自己加鎖,自己釋放鎖記得一定要釋放,不然其他線

前言

今天講的多線程的同步控制
直接進(jìn)入正題

ReentrantLock重入鎖

重入鎖可以完全代替synchronized,它需要java.util.concurrent.locks.ReentrantLock類(lèi)來(lái)實(shí)現(xiàn)
下面用一個(gè)簡(jiǎn)單的例子來(lái)實(shí)現(xiàn)重入鎖:

public class ReentrantLockThread implements Runnable{
    public static ReentrantLock lock = new ReentrantLock();
    public static int i = 0;
    @Override
    public void run() {
        for (int j=0;j<10000;j++){
            lock.lock();
            try {
                i++;
            }finally {
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ReentrantLockThread thread = new ReentrantLockThread();
        Thread t1 = new Thread(thread);
        Thread t2 = new Thread(thread);
        t1.start();t2.start();
        t1.join();t2.join();
        System.out.println(i);
    }
}

以上代碼打印出來(lái)的i是20000,可以說(shuō)明ReentrantLock也實(shí)現(xiàn)了線程同步
它相比synchronized更加靈活,因?yàn)橹厝腈i實(shí)現(xiàn)了用戶自己加鎖.lock(),自己釋放鎖.unlock()(記得一定要釋放,不然其他線程無(wú)法進(jìn)入)
當(dāng)然重入鎖同一個(gè)對(duì)象可以加兩個(gè)鎖,但也要記得釋放兩個(gè)鎖(多釋放了會(huì)拋出異常,少釋放了那其它線程就進(jìn)不來(lái))

重入鎖的中斷功能也是它的高級(jí)功能之一:
在run()代碼塊中寫(xiě)入lock.lockInterruptibly()方法,當(dāng)線程實(shí)例使用t1.interrupt()時(shí),外部通知便會(huì)就會(huì)中斷t1線程
下面來(lái)一個(gè)簡(jiǎn)單示例代碼Demo:

public class interruptTest {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(){
            public void run(){
                while (true){
                    System.out.println("go");
                    if (Thread.currentThread().isInterrupted()){
                        break;
                    }
                }
            }
        };
        t1.start();
        Thread.sleep(10001);
        t1.interrupt();
    }
}

發(fā)現(xiàn)t1線程實(shí)現(xiàn)interrupt()方法時(shí),線程實(shí)現(xiàn)代碼中的.isInterrupted()執(zhí)行了,并且中斷了t1線程
中斷功能可以很好的防止兩個(gè)線程間互相等待,出現(xiàn)死鎖的現(xiàn)象。

除了.interrupt()通知,要避免死鎖的另一種方法,就是限時(shí)等待:lock.tryLock()
我們來(lái)看下代碼:

public class MyThread implements Runnable{
    public static ReentrantLock lock = new ReentrantLock();
    public static int i = 0;
    @Override
    public void run() {
        try {
            if (lock.tryLock(5, TimeUnit.SECONDS)){
                Thread.sleep(6000);
            }else {
                System.out.println("結(jié)束線程");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if (lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        Thread t1 = new Thread(thread);
        Thread t2 = new Thread(thread);
        t1.start();
        t2.start();
    }
}

lock.tryLock(5, TimeUnit.SECONDS)解釋下:
如果超過(guò)5秒還沒(méi)有得到鎖,就返回false,執(zhí)行else語(yǔ)句
如果成功獲得鎖,就返回true,執(zhí)行sleep語(yǔ)句
所以5秒后打印結(jié)束線程,結(jié)束的就是等待5秒后沒(méi)有拿到鎖的線程

當(dāng)然也可以不帶等待時(shí)間,直接if(lock.tryLock())

下面是對(duì)ReentrantLock的整理

lock():獲得鎖,如果鎖被占用,則等待
lockInterruptibly():獲得鎖,但優(yōu)先響應(yīng)中斷
tryLock():獲得鎖,如果成功返回true,如果失敗返回false
unlock():釋放鎖
Condition條件

Condition是和ReentrantLock關(guān)聯(lián)的
利用綁定的Condition我們可以讓線程在合適的時(shí)間等待,或者在某一特定時(shí)刻獲得通知繼續(xù)執(zhí)行
下面演示一段簡(jiǎn)單的Condition代碼

public class MyThread implements Runnable{
    public static ReentrantLock lock = new ReentrantLock();
    public static Condition condition = lock.newCondition();
    @Override
    public void run() {
        try {
            lock.lock();
            Thread.sleep(1000);
            System.out.println("t1拿到鎖,接著進(jìn)入等待,并且釋放鎖");
            condition.await();
            Thread.sleep(4000);
            System.out.println("t1又拿到鎖");
        } catch (InterruptedException e) {
        }finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyThread thread = new MyThread();
        Thread t1 = new Thread(thread);
        t1.start();
        Thread.sleep(5000);
        lock.lock();
        System.out.println("主線程占用鎖");
        condition.signal();
        System.out.println("喚醒成功,主線程釋放鎖");
        lock.unlock();
    }
}

await():會(huì)讓當(dāng)前線程進(jìn)入等待并且釋放鎖
singal();會(huì)喚醒一個(gè)在等待中的線程,當(dāng)然執(zhí)行方法的線程必須釋放鎖,被喚醒的線程才能得到鎖

Semaphore信號(hào)量

怎樣才能規(guī)定進(jìn)入一段代碼的線程數(shù)
這里我們使用信號(hào)量,在外面定義Semaphore semp = new Semaphore(10);,這樣簡(jiǎn)單的設(shè)定了5個(gè)線程
在run()方法中semp.acquire();表示獲得了10個(gè)中的其中一個(gè)許可證
當(dāng)你的工作代碼完成時(shí),依舊在run()方法的后面寫(xiě)上semp.release();,許可證被釋放(跟鎖一個(gè)道理)

ReadWriteLock讀寫(xiě)鎖

我們知道讀不會(huì)響應(yīng)數(shù)據(jù),寫(xiě)會(huì)影響數(shù)據(jù)
所以我們真正操作的時(shí)候要求只讀的那些線程可以一起執(zhí)行,不用同步操作
而與寫(xiě)有關(guān)的線程全部要同步,以保護(hù)數(shù)據(jù)的安全
那么我們?cè)鯓硬拍茏龅街蛔x線程不用同步呢
這里需要用到讀寫(xiě)鎖,下面演示一段讀寫(xiě)鎖的簡(jiǎn)單例子:

public class ReadWriteThread {
    private static Lock lock = new ReentrantLock();
    private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private static Lock readLock = readWriteLock.readLock();
    private static Lock writeLock = readWriteLock.writeLock();
    private int value;

    //讀
    public int handleRead(Lock lock) throws InterruptedException{
        try {
            lock.lock();
            Thread.sleep(5000); //模擬讀線程
            System.out.println("讀完成");
            return value;
        } finally {
            lock.unlock();
        }
    }
    //寫(xiě)
    public void handleWrite(Lock lock,int index) throws InterruptedException {
        try {
            lock.lock();
            Thread.sleep(5000);
            value=index;
            System.out.println("寫(xiě)完成");
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ReadWriteThread demo = new ReadWriteThread();
        Thread t1 = new Thread(){
            public void run(){
                try {
                    demo.handleRead(readLock);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        Thread t2 = new Thread(){
            public void run(){
                try {
                    demo.handleRead(readLock);
                    demo.handleWrite(writeLock,2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t1.start();
        t2.start();
    }
}

執(zhí)行結(jié)果可以發(fā)現(xiàn),對(duì)于只讀的方法,我們給予readLock鎖,而寫(xiě)的方法給予writeLock鎖
如此這般,只讀的方法可以并行,而讀寫(xiě)只能串行

CountDownLatch倒計(jì)時(shí)器

倒計(jì)時(shí)器用來(lái)控制線程等待,它可以讓一個(gè)線程等待直到倒計(jì)時(shí)結(jié)束,再開(kāi)始執(zhí)行
還是通過(guò)實(shí)例來(lái)讓大家知道什么是倒計(jì)時(shí)器,并且它能有什么作用
這個(gè)例子的需求是:要在主線程之前完成之個(gè)類(lèi)線程才能繼續(xù)主線程:

public class CountDownLatchThread implements Runnable{
    private static CountDownLatch countDownLatch = new CountDownLatch(10);
    private static CountDownLatchThread countDownLatchThread = new CountDownLatchThread();

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println("此線程工作完成");
            countDownLatch.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);    //創(chuàng)建一個(gè)固定大小為10的線程池
        for (int i=0;i<10;i++){
            executorService.submit(countDownLatchThread);
        }
        countDownLatch.await();
        System.out.println("10個(gè)任務(wù)完成");
        executorService.shutdown();
    }
}

countDownLatch.await();:讓主線程進(jìn)入等待,等待所有10個(gè)線程完成
countDownLatch.countDown();:說(shuō)明這個(gè)線程已完成,并進(jìn)入統(tǒng)計(jì)

線程池

線程池和數(shù)據(jù)庫(kù)連接池一樣,事先準(zhǔn)備好線程,當(dāng)程序需要線程時(shí),調(diào)用線程池中空閑的線程,當(dāng)工作線程工作完畢時(shí),重新放回線程池
JDK提供了一套Executor框架,本質(zhì)是線程池。
框架中的成員變量在java.util.concurrent包中,是JDK并發(fā)包的核心類(lèi)
其中ThreadPoolExecutor實(shí)現(xiàn)了Executor接口,任何Runnable都可以被ThreadPoolExecutor線程池調(diào)度

Executor提供了各種類(lèi)型的線程池,主要有以下工廠方法創(chuàng)建不同的線程池:
newFixedThreadPool()方法:返回一個(gè)固定線程數(shù)量的線程池,當(dāng)一個(gè)新任務(wù)提交時(shí),如果有空閑線程,立即執(zhí)行,如果沒(méi)有空閑線程,新任務(wù)會(huì)被放在一個(gè)等待隊(duì)列中去等待空閑線程
newCachedThreadPool()方法:返回一個(gè)根據(jù)實(shí)際情況調(diào)整線程數(shù)量的線程池,
newSingleThreadScheduledExecutor()方法:返回一個(gè)對(duì)象,可放線程數(shù)量為1,但是這個(gè)線程池拓展了計(jì)劃任務(wù)功能,可以規(guī)定執(zhí)行時(shí)間、周期性等等

上面這些線程池的源碼其實(shí)都是用ThreadPoolExecutor實(shí)現(xiàn):
我們來(lái)看下ThreadPoolExecutor的構(gòu)造函數(shù):

public ThreadPoolExecutor(int corePoolSize,    //指定線程池中線程的數(shù)量
                          int maximumPoolSize,//指定線程池中最大線程數(shù)量        
                          long keepAliveTime,    //當(dāng)線程池中線程數(shù)量超過(guò)corePoolSize時(shí),多余線程的存活時(shí)間
                          TimeUnit unit,    //keepAliveTime的單位
                          BlockingQueue workQueue,    //等待任務(wù)隊(duì)列,被提交都是尚未執(zhí)行的任務(wù)
                          ThreadFactory threadFactory,    //線程工廠,用于創(chuàng)建線程,一般默認(rèn)
                          RejectedExecutionHandler handler    //拒絕策略,當(dāng)任務(wù)太多時(shí),如何拒絕任務(wù)
    }

下面我來(lái)講講BlockingQueue:
在ThreadPoolExecutor構(gòu)造器中,有以下幾種BlockingQueue:
1.直接提交隊(duì)列:有SynchronousQueue對(duì)象提供,提交的任務(wù)如果沒(méi)有空閑線程嘗試新建線程,如果線程數(shù)量已達(dá)到最大,則執(zhí)行拒絕策略
2.有界的任務(wù)隊(duì)列:使用ArrayBlockingQueue對(duì)象實(shí)現(xiàn),構(gòu)造器帶一個(gè)任務(wù)的容量參數(shù),若等待隊(duì)列已滿,總線程不大于maximumPoolSize的前提下,創(chuàng)建新的線程執(zhí)行任務(wù),若大于,則執(zhí)行拒絕策略
3.無(wú)界的任務(wù)隊(duì)列:使用LinkedBlockingQueue來(lái)實(shí)現(xiàn),和有界相比,除非系統(tǒng)資源耗盡,否則無(wú)界的任務(wù)隊(duì)列不存在任務(wù)入隊(duì)失敗的情況
4.有限任務(wù)隊(duì)列:通過(guò)PriorityBlockingQueue實(shí)現(xiàn),可以控制任務(wù)的執(zhí)行先后順序

再來(lái)看newFixedThreadPool(),它使用了無(wú)界任務(wù)隊(duì)列,并且corePoolSize和maximumPoolSize一樣大,因?yàn)閷?duì)于固定大小的線程池,不存在線程數(shù)量的動(dòng)態(tài)變化,當(dāng)任務(wù)提交非常頻繁時(shí),可能會(huì)耗盡系統(tǒng)資源

而newCachedThreadPool()方法返回corePoolSize為0,maximumPoolSize無(wú)限大的線程池,使用了SynchronousQueue隊(duì)列,當(dāng)任務(wù)執(zhí)行完畢后,由于corePoolSize為0,空閑線程會(huì)在指定時(shí)間(60s)回收

講完了BlockingQueue我們來(lái)講下RejectedExecutionHandler拒絕策略
JDK內(nèi)置了四種拒絕策略:
1.AbortPolicy:直接拋出異常,阻止系統(tǒng)正常工作
2.CallerRunsPolicy:只要線程池沒(méi)有關(guān)閉,該策略直接調(diào)用工作線程運(yùn)行當(dāng)前被丟棄的任務(wù)
3.DiscardOledestPolicy:丟棄最老的一個(gè)等待任務(wù),也就是即將被執(zhí)行的一個(gè)任務(wù),并嘗試再次提交當(dāng)前任務(wù)
4.DiscardPolicy:默默地丟棄無(wú)法處理的任務(wù),如果運(yùn)行任務(wù)丟失,這是最好的一個(gè)策略

當(dāng)然我們也可以自己寫(xiě)拒絕策略
下面我來(lái)寫(xiě)一個(gè)打印出被拒絕的策略(而不是選擇拋異常,因?yàn)閽伄惓N覀冞€要去捕捉異常,如果沒(méi)有捕捉到會(huì)導(dǎo)致系統(tǒng)奔潰)

public class ThreadPoolTest {
    public static class TestThread implements Runnable{
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis()+"線程ID:"+Thread.currentThread().getId());
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestThread testThread = new TestThread();
        ExecutorService es = new ThreadPoolExecutor(5,5,0L, TimeUnit.SECONDS,
                                                    new ArrayBlockingQueue(10), Executors.defaultThreadFactory(),
                                                    new RejectedExecutionHandler(){
                                                        @Override
                                                        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                                                            System.out.println("等待線程被拒絕");
                                                        }
                                                    });
        for (int i=0;i

來(lái)看下運(yùn)行結(jié)果:

1511833277724線程ID:13
1511833277771線程ID:10
1511833277771線程ID:14
1511833277771線程ID:12
等待線程被拒絕
等待線程被拒絕
1511833277833線程ID:13
1511833277833線程ID:11

可以發(fā)現(xiàn),我們自定義的線程池和拒絕策略完美的執(zhí)行了

并發(fā)高效容器

下面我介紹給大家?guī)讉€(gè)非常好用的工具類(lèi)(當(dāng)然都是線程安全的)
1.ConcurrentHashMap:這是一個(gè)高效的hashMap
2.CopyOnwriteArrayList:在讀多寫(xiě)少的場(chǎng)合這個(gè)list非常好用,遠(yuǎn)勝與Vector
3.ConCurrentLinkedQueue:高效的并發(fā)隊(duì)列,使用鏈表實(shí)現(xiàn),可以看做是一個(gè)線程安全的LinkedList
4.BlockingQueue:這個(gè)接口上面說(shuō)過(guò),表示阻塞隊(duì)列,非常適合用于作為數(shù)據(jù)共享的通道
5.ConcurrentSkipListMap:這是一個(gè)Map,使用跳表的數(shù)據(jù)結(jié)構(gòu)進(jìn)行快速的查找

如果并不追求高效,我們可以使用Collections類(lèi)幫助把線程不安全的容器轉(zhuǎn)換為線程安全
如將HashMap轉(zhuǎn)換為線程安全:

Map map = Collections.synchronizedMap(new HashMap());

當(dāng)然可以使用CAS操作來(lái)替代synchronized

今天就先到這里,大家可以看看這些內(nèi)容的拓展
記得點(diǎn)關(guān)注看更新,謝謝閱讀

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

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

相關(guān)文章

  • java并發(fā)之從零到放棄(一)

    摘要:今天就先到這里,大家可以看看這些內(nèi)容的拓展記得點(diǎn)關(guān)注看更新,謝謝閱讀 前言 這是一個(gè)長(zhǎng)篇博客,希望大家關(guān)注我并且一起學(xué)習(xí)java高并發(fā)廢話不多說(shuō),直接開(kāi)始 并行和并發(fā) 并行:多個(gè)線程同時(shí)處理多個(gè)任務(wù)并發(fā):多個(gè)線程處理同個(gè)任務(wù),不一定要同時(shí) 下面用圖來(lái)描述并行和并發(fā)的區(qū)別:(實(shí)現(xiàn)和虛線表示兩個(gè)不同的線程) showImg(https://segmentfault.com/img/bVYT...

    luoyibu 評(píng)論0 收藏0
  • java并發(fā)之從零到放棄(二)

    摘要:可以用代替可以用代替定義的對(duì)象的值是不可變的今天就先到這里,大家可以看看這些內(nèi)容的拓展記得點(diǎn)關(guān)注看更新,謝謝閱讀 前言 java高并發(fā)第二篇講的是java線程的基礎(chǔ)依舊不多說(shuō)廢話 線程和進(jìn)程 進(jìn)程是操作系統(tǒng)運(yùn)行的基礎(chǔ),是一個(gè)程序運(yùn)行的實(shí)體,windows上打開(kāi)任務(wù)管理器就能看到進(jìn)程線程是輕量級(jí)的進(jìn)程,是程序執(zhí)行的最小單位,是在進(jìn)程這個(gè)容器下進(jìn)行的 線程基本操作 新建一個(gè)線程類(lèi)有兩種方式...

    cloud 評(píng)論0 收藏0
  • java并發(fā)之從零到放棄(五)

    摘要:前言這篇主要來(lái)講解多線程中一個(gè)非常經(jīng)典的設(shè)計(jì)模式包括它的基礎(chǔ)到拓展希望大家能夠有所收獲生產(chǎn)者消費(fèi)者模式簡(jiǎn)述此設(shè)計(jì)模式中主要分兩類(lèi)線程生產(chǎn)者線程和消費(fèi)者線程生產(chǎn)者提供數(shù)據(jù)和任務(wù)消費(fèi)者處理數(shù)據(jù)和任務(wù)該模式的核心就是數(shù)據(jù)和任務(wù)的交互點(diǎn)共享內(nèi)存緩 前言 這篇主要來(lái)講解多線程中一個(gè)非常經(jīng)典的設(shè)計(jì)模式包括它的基礎(chǔ)到拓展希望大家能夠有所收獲 生產(chǎn)者-消費(fèi)者模式簡(jiǎn)述 此設(shè)計(jì)模式中主要分兩類(lèi)線程:生產(chǎn)者...

    meislzhua 評(píng)論0 收藏0
  • java并發(fā)之從零到放棄(四)

    摘要:前言本篇主要講解如何去優(yōu)化鎖機(jī)制或者克服多線程因?yàn)殒i可導(dǎo)致性能下降的問(wèn)題線程變量有這樣一個(gè)場(chǎng)景,前面是一大桶水,個(gè)人去喝水,為了保證線程安全,我們要在杯子上加鎖導(dǎo)致大家輪著排隊(duì)喝水,因?yàn)榧恿随i的杯子是同步的,只能有一個(gè)人拿著這個(gè)唯一的杯子喝 前言 本篇主要講解如何去優(yōu)化鎖機(jī)制或者克服多線程因?yàn)殒i可導(dǎo)致性能下降的問(wèn)題 ThreadLocal線程變量 有這樣一個(gè)場(chǎng)景,前面是一大桶水,10個(gè)...

    Alex 評(píng)論0 收藏0
  • 前端從零開(kāi)始系列

    摘要:只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙只有動(dòng)手,你才能真正掌握一門(mén)技術(shù)持續(xù)更新中項(xiàng)目地址求求求源碼系列跟一起學(xué)如何寫(xiě)函數(shù)庫(kù)中高級(jí)前端面試手寫(xiě)代碼無(wú)敵秘籍如何用不到行代碼寫(xiě)一款屬于自己的類(lèi)庫(kù)原理講解實(shí)現(xiàn)一個(gè)對(duì)象遵循規(guī)范實(shí)戰(zhàn)手摸手,帶你用擼 Do it yourself!!! 只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙 只有動(dòng)手,你才能真正掌握一門(mén)技術(shù) 持續(xù)更新中…… 項(xiàng)目地址 https...

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

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

0條評(píng)論

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