亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

(二)線程的應(yīng)用及挑戰(zhàn)

hqman / 1361人閱讀

摘要:上下文切換會影響到線程的執(zhí)行速度,對于系統(tǒng)來說意味著會消耗大量的時間減少上下文切換的方式無鎖并發(fā)編程,在多線程競爭鎖時,會導(dǎo)致大量的上下文切換。線程在中的使用在中實(shí)現(xiàn)多線程的方式比較簡單,因?yàn)橹刑峁┝朔浅7奖愕膩韺?shí)現(xiàn)多線程。

文章簡介

上一篇文章我們了解了進(jìn)程和線程的發(fā)展歷史、線程的生命周期、線程的優(yōu)勢和使用場景,這一篇,我們從Java層面更進(jìn)一步了解線程的使用

內(nèi)容導(dǎo)航

并發(fā)編程的挑戰(zhàn)

線程在Java中的使用

并發(fā)編程的挑戰(zhàn)

引入多線程的目的在第一篇提到過,就是為了充分利用CPU是的程序運(yùn)行得更快,當(dāng)然并不是說啟動的線程越多越好。在實(shí)際使用多線程的時候,會面臨非常多的挑戰(zhàn)

線程安全問題

線程安全問題值的是當(dāng)多個線程訪問同一個對象時,如果不考慮這些運(yùn)行時環(huán)境采用的調(diào)度方式或者這些線程將如何交替執(zhí)行,并且在代碼中不需要任何同步操作的情況下,這個類都能夠表現(xiàn)出正確的行為,那么這個類就是線程安全的
比如下面的代碼是一個單例模式,在代碼的注釋出,如果多個線程并發(fā)訪問,則會出現(xiàn)多個實(shí)例。導(dǎo)致無法實(shí)現(xiàn)單例的效果

public class SingletonDemo {
   private static SingletonDemo singletonDemo=null;
   private SingletonDemo(){}
    public static SingletonDemo getInstance(){
        if(singletonDemo==null){/***線程安全問題***/
           singletonDemo=new SingletonDemo();
        }
        return singletonDemo;
    }
}

通常來說,我們把多線程編程中的線程安全問題歸類成如下三個,至于每一個問題的本質(zhì),在后續(xù)的文章中我們會多帶帶講解

原子性

可見性

有序性

上下文切換問題

在單核心CPU架構(gòu)中,對于多線程的運(yùn)行是基于CPU時間片切換來實(shí)現(xiàn)的偽并行。由于時間片非常短導(dǎo)致用戶以為是多個線程并行執(zhí)行。而一次上下文切換,實(shí)際就是當(dāng)前線程執(zhí)行一個時間片之后切換到另外一個線程,并且保存當(dāng)前線程執(zhí)行的狀態(tài)這個過程。上下文切換會影響到線程的執(zhí)行速度,對于系統(tǒng)來說意味著會消耗大量的CPU時間

減少上下文切換的方式

無鎖并發(fā)編程,在多線程競爭鎖時,會導(dǎo)致大量的上下文切換。避免使用鎖去解決并發(fā)問題可以減少上下文切換

CAS算法,CAS是一種樂觀鎖機(jī)制,不需要加鎖

使用與硬件資源匹配合適的線程數(shù)

死鎖

在解決線程安全問題的場景中,我們會比較多的考慮使用鎖,因?yàn)樗褂帽容^簡單。但是鎖的使用如果不恰當(dāng),則會引發(fā)死鎖的可能性,一旦產(chǎn)生死鎖,就會造成比較嚴(yán)重的問題:產(chǎn)生死鎖的線程會一直占用鎖資源,導(dǎo)致其他嘗試獲取鎖的線程也發(fā)生死鎖,造成系統(tǒng)崩潰

以下是死鎖的簡單案例

public class DeadLockDemo {
    //定義鎖對象
    private final Object lockA = new Object();
    private final Object lockB = new Object();
    private void deadLock(){
        new Thread(()->{
            synchronized (lockA){
                try {
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockB){
                    System.out.println("Lock B");
                }
            }
        }).start();
        new Thread(()->{
            synchronized (lockB){
                synchronized (lockA){
                    System.out.println("Lock A");
                }
            }
        }).start();
    }
    public static void main(String[] args) {
        new DeadLockDemo().deadLock();
    }
}
通過jstack分析死鎖

1.首先通過jps獲取當(dāng)前運(yùn)行的進(jìn)程的pid

6628 Jps
17588 RemoteMavenServer
19220 Launcher
19004 DeadLockDemo

2.jstack打印堆棧信息,輸入 jstack19004, 會打印如下日志,可以很明顯看到死鎖的信息提示

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x000000001d461e68 (object 0x000000076b310df8, a java.lang.Object),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x000000001d463258 (object 0x000000076b310e08, a java.lang.Object),
  which is held by "Thread-1"
解決死鎖的手段
1.保證多個線程按照相同的順序獲取鎖
2.設(shè)置獲取鎖的超時時間,超過設(shè)定時間以后自動釋放
3.死鎖檢測
資源限制

資源限制主要指的是硬件資源和軟件資源,在開發(fā)多線程應(yīng)用時,程序的執(zhí)行速度受限于這兩個資源。硬件的資源限制無非就是磁盤、CPU、內(nèi)存、網(wǎng)絡(luò);軟件資源的限制有很多,比如數(shù)據(jù)庫連接數(shù)、計(jì)算機(jī)能夠支持的最大連接數(shù)等
資源限制導(dǎo)致的問題最直觀的體現(xiàn)就是前面說的上下文切換,也就是CPU資源和線程資源的嚴(yán)重不均衡導(dǎo)致頻繁上下文切換,反而會造成程序的運(yùn)行速度下降

資源限制的主要解決方案,就是缺啥補(bǔ)啥。CPU不夠用,可以增加CPU核心數(shù);一臺機(jī)器的資源有限,則增加多臺機(jī)器來做集群。
線程在Java中的使用

在Java中實(shí)現(xiàn)多線程的方式比較簡單,因?yàn)镴ava中提供了非常方便的API來實(shí)現(xiàn)多線程。
1.繼承Thread類實(shí)現(xiàn)多線程
2.實(shí)現(xiàn)Runnable接口
3.實(shí)現(xiàn)Callable接口通過Future包裝器來創(chuàng)建Thread線程,這種是帶返回值的線程
4.使用線程池ExecutorService

繼承Thread類

繼承Thread類,然后重寫run方法,在run方法中編寫當(dāng)前線程需要執(zhí)行的邏輯。最后通過線程實(shí)例的start方法來啟動一個線程

public class ThreadDemo extends Thread{
    @Override
    public void run() {
        //重寫run方法,提供當(dāng)前線程執(zhí)行的邏輯
        System.out.println("Hello world");
    }
    public static void main(String[] args) {
        ThreadDemo threadDemo=new ThreadDemo();
        threadDemo.start();
    }
}
Thread類其實(shí)是實(shí)現(xiàn)了Runnable接口,因此Thread自己也是一個線程實(shí)例,但是我們不能直接用 newThread().start()去啟動一個線程,原因很簡單,Thread類中的run方法是沒有實(shí)際意義的,只是一個調(diào)用通過構(gòu)造函數(shù)傳遞寄來的另一個Runnable實(shí)現(xiàn)類的run方法,這塊的具體演示會在Runnable接口的代碼中看到
public
class Thread implements Runnable {
    /* What will be run. */
    private Runnable target;
    ...
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
    ...
實(shí)現(xiàn)Runnable接口

如果需要使用線程的類已經(jīng)繼承了其他的類,那么按照J(rèn)ava的單一繼承原則,無法再繼承Thread類來實(shí)現(xiàn)線程,所以可以通過實(shí)現(xiàn)Runnable接口來實(shí)現(xiàn)多線程

public class RunnableDemo implements Runnable{
    @Override
    public void run() {
        //重寫run方法,提供當(dāng)前線程執(zhí)行的邏輯
        System.out.println("Hello world");
    }
    public static void main(String[] args) {
        RunnableDemo runnableDemo=new RunnableDemo();
        new Thread(runnableDemo).start();
    }
}
上面的代碼中,實(shí)現(xiàn)了Runnable接口,重寫了run方法;接著為了能夠啟動RunnableDemo這個線程,必須要實(shí)例化一個Thread類,通過構(gòu)造方法傳遞一個Runnable接口實(shí)現(xiàn)類去啟動,Thread的run方法就會調(diào)用target.run來運(yùn)行當(dāng)前線程,代碼在上面.
實(shí)現(xiàn)Callable接口

在有些多線程使用的場景中,我們有時候需要獲取異步線程執(zhí)行完畢以后的反饋結(jié)果,也許是主線程需要拿到子線程的執(zhí)行結(jié)果來處理其他業(yè)務(wù)邏輯,也許是需要知道線程執(zhí)行的狀態(tài)。那么Callable接口可以很好的實(shí)現(xiàn)這個功能

public class CallableDemo implements Callable{
    @Override
    public String call() throws Exception {
        return "hello world";
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable callable=new CallableDemo();
        FutureTask task=new FutureTask<>(callable);
        new Thread(task).start();
        System.out.println(task.get());//獲取線程的返回值
    }
}
在上面代碼案例中的最后一行 task.get()就是獲取線程的返回值,這個過程是阻塞的,當(dāng)子線程還沒有執(zhí)行完的時候,主線程會一直阻塞直到結(jié)果返回
使用線程池

為了減少頻繁創(chuàng)建線程和銷毀線程帶來的性能開銷,在實(shí)際使用的時候我們會采用線程池來創(chuàng)建線程,在這里我不打算展開多線程的好處和原理,我會在后續(xù)的文章中多帶帶說明。

public class ExecutorServiceDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //創(chuàng)建一個固定線程數(shù)的線程池
        ExecutorService pool = Executors.newFixedThreadPool(1);
        Future future=pool.submit(new CallableDemo()); 
        System.out.println(future.get());
    }
}
pool.submit有幾個重載方法,可以傳遞帶返回值的線程實(shí)例,也可以傳遞不帶返回值的線程實(shí)例,源代碼如下
/*01*/Future submit(Runnable task);
/*02*/ Future submit(Runnable task, T result);
/*03*/ Future submit(Callable task);

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

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/72577.html

相關(guān)文章

  • 實(shí)時聯(lián)網(wǎng)游戲后臺服務(wù)技術(shù)選型與挑戰(zhàn)(網(wǎng)絡(luò)接入篇)

    摘要:概述本系列文章將從開發(fā)者角度梳理開發(fā)實(shí)時聯(lián)網(wǎng)游戲后臺服務(wù)過程中可能面臨的挑戰(zhàn),并針對性地提供相應(yīng)解決思路,期望幫助開發(fā)者依據(jù)自身游戲特點(diǎn)做出合理的技術(shù)選型。多路復(fù)用避免了讀寫阻塞,減少了上下文切換,提升了利用率和系統(tǒng)吞吐率。 概述:本系列文章將從開發(fā)者角度梳理開發(fā)實(shí)時聯(lián)網(wǎng)游戲后臺服務(wù)過程中可能面臨的挑戰(zhàn),并針對性地提供相應(yīng)解決思路,期望幫助開發(fā)者依據(jù)自身游戲特點(diǎn)做出合理的技術(shù)選型。 關(guān)...

    zhisheng 評論0 收藏0
  • TiDB 在平安核心系統(tǒng)引入應(yīng)用

    摘要:年月日,平安科技數(shù)據(jù)庫產(chǎn)品資深工程師何志勇在第十屆數(shù)據(jù)庫技術(shù)大會上分享了在平安核心系統(tǒng)的引入及應(yīng)用,通過對進(jìn)行測試,詳細(xì)解析如何選擇適用于金融行業(yè)級別的開源分布式數(shù)據(jù)庫,以及平安財(cái)神節(jié)活動中引入的全流程應(yīng)用實(shí)踐案例分享。 作者:何志勇本文轉(zhuǎn)載自公眾號「平安科技數(shù)據(jù)庫產(chǎn)品團(tuán)隊(duì)」。 2019 年 5 月 9 日,平安科技數(shù)據(jù)庫產(chǎn)品資深工程師何志勇在第十屆數(shù)據(jù)庫技術(shù)大會 DTCC 上分享了《...

    hss01248 評論0 收藏0

發(fā)表評論

0條評論

hqman

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<