小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

深入理解Java多線程與并發(fā)編程

 觀審美2 2019-06-19

一、多線程三大特性

多線程有三大特性:原子性、可見(jiàn)性、有序性。

原子性

(跟數(shù)據(jù)庫(kù)的事務(wù)特性中的原子性類似,數(shù)據(jù)庫(kù)的原子性體現(xiàn)是dml語(yǔ)句執(zhí)行后需要進(jìn)行提交):
理解:即一個(gè)操作或多個(gè)操作,要么全部執(zhí)行并且執(zhí)行的過(guò)程中不會(huì)被任何因素打斷,要么都不執(zhí)行。
一個(gè)很經(jīng)典的例子就是銀行賬戶轉(zhuǎn)賬問(wèn)題:
比如從賬戶A向賬戶B轉(zhuǎn)1000元,那么必然包括2個(gè)操作:從賬戶A減去1000元,往賬戶B加上1000元。這2個(gè)操作必須要具備原子性才能保證不出現(xiàn)一些意外的問(wèn)題。
我們操作數(shù)據(jù)也是如此,比如i = i+1;其中就包括,讀取i的值,計(jì)算i,寫入i。這行代碼在Java中是不具備原子性的,則多線程運(yùn)行肯定會(huì)出問(wèn)題,所以也需要我們使用同步synchronized和lock鎖這些東西來(lái)確保這個(gè)特性了。
原子性其實(shí)就是保證數(shù)據(jù)一致、線程安全一部分,

可見(jiàn)性:

可見(jiàn)性是與java內(nèi)存模型息息相關(guān)的。
當(dāng)多個(gè)線程訪問(wèn)同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值。
若兩個(gè)線程在不同的cpu,那么線程1改變了i的值還沒(méi)刷新到主存,線程2又使用了i,那么這個(gè)i值肯定還是之前的,線程1對(duì)變量的修改線程2沒(méi)有看到,這就是可見(jiàn)性問(wèn)題。

有序性:

理解:程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。
一般來(lái)說(shuō),處理器為了提高程序運(yùn)行效率,可能會(huì)對(duì)輸入代碼進(jìn)行優(yōu)化,它不保證程序中各個(gè)語(yǔ)句的執(zhí)行先后順序同代碼中的順序一致,但是它會(huì)保證程序最終執(zhí)行結(jié)果和代碼順序執(zhí)行的結(jié)果是一致的。

例如:

1
2
3
4
int a = 10; //語(yǔ)句1
int r = 2; //語(yǔ)句2
a = a + 3; //語(yǔ)句3
r = a*a;  //語(yǔ)句4

因?yàn)橹嘏判?,他還可能執(zhí)行順序?yàn)?2-1-3-4,1-3-2-4
但絕不可能 2-1-4-3,因?yàn)檫@打破了依賴關(guān)系。
顯然重排序?qū)尉€程運(yùn)行是不會(huì)有任何問(wèn)題,而多線程就不一定了,所以我們?cè)诙嗑€程編程時(shí)就得考慮這個(gè)問(wèn)題了。
多線程中保證有序性的方法:join()

二、Java內(nèi)存模型

jvm的內(nèi)存結(jié)構(gòu)為:堆、棧、方法區(qū),不同于java的內(nèi)存模型,Java的內(nèi)存模型是關(guān)于多線程相關(guān)的。

理解:共享內(nèi)存模型指的是Java內(nèi)存模型(簡(jiǎn)稱JMM),JMM決定一個(gè)線程對(duì)共享變量的寫入時(shí),能對(duì)另一個(gè)線程可見(jiàn)。從抽象的角度來(lái)看,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:線程之間的共享變量存儲(chǔ)在主內(nèi)存(main memory)中(局部變量不會(huì)存儲(chǔ)在),每個(gè)線程都有一個(gè)私有的本地內(nèi)存(local memory),本地內(nèi)存中存儲(chǔ)了該線程以讀/寫共享變量的副本。本地內(nèi)存是JMM的一個(gè)抽象概念,并不真實(shí)存在。它涵蓋了緩存、寫緩沖區(qū)、寄存器以及其他的硬件和編輯器優(yōu)化。

總結(jié):什么是Java內(nèi)存模型:java內(nèi)存模型簡(jiǎn)稱jmm,定義了一個(gè)線程對(duì)另一個(gè)線程可見(jiàn)。共享變量存放在主內(nèi)存中,每個(gè)線程都有自己的本地內(nèi)存,當(dāng)多個(gè)線程同時(shí)訪問(wèn)一個(gè)數(shù)據(jù)的時(shí)候,可能本地內(nèi)存沒(méi)有及時(shí)刷新到主內(nèi)存,所以就會(huì)發(fā)生線程安全問(wèn)題。

三、Volatile關(guān)鍵字

Volatile關(guān)鍵字的作用:變量在多個(gè)線程之間可見(jiàn)。

Volatile關(guān)鍵字是非原子性的,不能保證數(shù)據(jù)的原子性,只是能夠把解決立馬刷新到主內(nèi)存中,不能解決并發(fā)問(wèn)題。

如果想要保證數(shù)據(jù)的原子性,解決并發(fā)問(wèn)題,需要使用并發(fā)包里的AutomicInteger原子類。

volatile與synchronized區(qū)別:
僅靠volatile不能保證線程的安全性(原子性)。

  1. 1.volatile輕量級(jí),只能修飾變量。synchronized重量級(jí),還可修飾方法。

  2. 2.volatile只能保證數(shù)據(jù)的可見(jiàn)性,不能用來(lái)同步,因?yàn)槎鄠€(gè)線程并發(fā)訪問(wèn)volatile修飾的變量不會(huì)阻塞。

synchronized不僅保證可見(jiàn)性,而且還保證原子性,因?yàn)橹挥蝎@得了鎖的線程才能進(jìn)入臨界區(qū),從而保證臨界區(qū)中的所有語(yǔ)句都全部執(zhí)行。多個(gè)線程爭(zhēng)搶synchronized鎖對(duì)象時(shí)會(huì)出現(xiàn)阻塞。

synchronized會(huì)把主內(nèi)存中的共享變量鎖住,永遠(yuǎn)只有一個(gè)線程操作主內(nèi)存的共享變量。

線程安全性包括兩個(gè)方便:1.可見(jiàn)性 2.原子性

僅僅使用volatile不能保證線程安全性,而synchronized則可實(shí)現(xiàn)線程的安全性。

代碼實(shí)現(xiàn):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package chauncy.concurrentprogramming;
class ThreadVolatile extends Thread {
    public volatile boolean flag = true;
    @Override
    public void run() {
        System.out.println("子線程開(kāi)始執(zhí)行...");
        while (flag) {
        }
        System.out.println("子線程結(jié)束執(zhí)行...");
    }
    public void isRun(boolean flag) {
        this.flag = flag;
    }
}
/**
 * @classDesc: 功能描述(Volatile關(guān)鍵字的使用)
 * @author: ChauncyWang
 * @createTime: 2019年3月12日 上午10:17:14
 * @version: 1.0
 */
public class Volatile {
    public static void main(String[] args) throws InterruptedException {
        ThreadVolatile threadVolatile1 = new ThreadVolatile();
        threadVolatile1.start();
        Thread.sleep(300);
        /**
         * 如果不對(duì)變量加Volatile關(guān)鍵字,則子線程不會(huì)停止運(yùn)行 原因:線程之間是不可見(jiàn)的,讀取的是副本,沒(méi)有及時(shí)讀取到主內(nèi)存結(jié)果。
         * 解決辦法:使用Volatile關(guān)鍵字解決線程之間的可見(jiàn)性,強(qiáng)制線程每次讀取該值的時(shí)候都去“主內(nèi)存”中取值。
         */
        threadVolatile1.isRun(false);
        System.out.println("flag:" + threadVolatile1.flag);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package chauncy.concurrentprogramming;
import java.util.concurrent.atomic.AtomicInteger;
class VolatileNoAtomicThread extends Thread {
    // private static volatile int count = 0;
    private static AtomicInteger atomicInteger = new AtomicInteger(0);
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            // count++;
            atomicInteger.incrementAndGet();// count++
        }
        System.out.println(getName() + "-----" + atomicInteger);
    }
}
/**
 * @classDesc: 功能描述(Volatile修飾不具有原子性(不具有同步性),不能解決線程安全問(wèn)題)
 * @author: ChauncyWang
 * @createTime: 2019年3月12日 上午10:39:30
 * @version: 1.0
 */
public class VolatileNoAtomic {
    public static void main(String[] args) {
        // 初始化10個(gè)線程
        VolatileNoAtomicThread[] volatileNoAtomicThread = new VolatileNoAtomicThread[10];
        for (int i = 0; i < volatileNoAtomicThread.length; i++) {
            // 創(chuàng)建每一個(gè)線程
            volatileNoAtomicThread[i] = new VolatileNoAtomicThread();
        }
        for (int i = 0; i < volatileNoAtomicThread.length; i++) {
            // 啟動(dòng)每一個(gè)線程
            volatileNoAtomicThread[i].start();
        }
    }
}

四、TreadLocal

1.什么是ThreadLocal?

ThreadLocal提高一個(gè)線程的局部變量,訪問(wèn)某個(gè)線程擁有自己局部變量。

當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程對(duì)應(yīng)的副本。

ThreadLocal接口方法有4個(gè):

  1. void set(Object value)設(shè)置當(dāng)前線程的線程局部變量的值;

  2. public Object get()該方法返回當(dāng)前線程所對(duì)應(yīng)的線程局部變量;

  3. public void remove()將當(dāng)前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK5.0新增的方法。需要指出的是,當(dāng)線程結(jié)束后,對(duì)應(yīng)該線程的局部變量將自動(dòng)被垃圾回收,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,但它可以加快內(nèi)存的回收速度;

  4. protected Object initialValue()返回該線程局部變量的初始值,該方法是一個(gè)protected的方法,顯然是為了讓子類覆蓋而設(shè)計(jì)的。這個(gè)方法是一個(gè)延遲調(diào)用方法,在線程第1次調(diào)用get()或set(Object)時(shí)才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的缺省實(shí)現(xiàn)直接返回一個(gè)null。

2.ThreadLocal底層實(shí)現(xiàn)原理:

ThreadLocal通過(guò)Thread.currentThread();獲取當(dāng)前線程

操作map集合:ThreadLocalMap

void set(Object value)就是Map.put(“當(dāng)前線程”,值);

public Object get()就是獲取ThreadLocalMap然后操作后返回。

代碼實(shí)現(xiàn):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package chauncy.concurrentprogramming;
class Res {
    // private int count=0;
    /*
     * 設(shè)置本地局部變量,和其他線程局部變量隔離開(kāi),互不影響
     */
    private ThreadLocal<Integer> count = new ThreadLocal<Integer>() {
        protected Integer initialValue() {
            // 設(shè)置當(dāng)前線程局部變量的初始化值
            return 0;
        };
    };
    /**
     *
     * @methodDesc: 功能描述(生成訂單號(hào))
     * @author: ChauncyWang
     * @param: @return
     * @createTime: 2019年3月12日 下午2:23:57
     * @returnType: int
     */
    public Integer getNum() {
        int count = this.count.get() + 1;
        this.count.set(count);
        return count;
    }
}
class ThreadLocalDemo extends Thread {
    private Res res;
    public ThreadLocalDemo(Res res) {
        this.res = res;
    }
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            try {
                Thread.sleep(30);
            } catch (Exception e) {
            }
            System.out.println(getName() + "----i:" + i + ",number:" + res.getNum());
        }
    }
}
/**
 * @classDesc: 功能描述(本地線程的使用:創(chuàng)建三個(gè)線程,每個(gè)線程生成自己獨(dú)立的序列號(hào))
 * @author: ChauncyWang
 * @createTime: 2019年3月12日 下午2:21:03
 * @version: 1.0
 */
public class ThreadLocalTest {
    public static void main(String[] args) {
        Res res = new Res();
        ThreadLocalDemo t1 = new ThreadLocalDemo(res);
        ThreadLocalDemo t2 = new ThreadLocalDemo(res);
        ThreadLocalDemo t3 = new ThreadLocalDemo(res);
        t1.start();
        t2.start();
        t3.start();
    }
}

五、線程池

1.為什么要使用線程池?

因?yàn)橐ㄟ^(guò)線程池來(lái)管理線程,啟動(dòng)或者停止一個(gè)線程非常耗費(fèi)資源,所以將線程交給線程池來(lái)管理能夠節(jié)約內(nèi)存。
一般在企業(yè)開(kāi)發(fā)當(dāng)中我們都使用線程池,通過(guò)spring去整合線程池,異步注解。

2.什么是線程池?

線程池是指在初始化一個(gè)多線程應(yīng)用程序過(guò)程中創(chuàng)建一個(gè)線程集合,然后在需要執(zhí)行新的任務(wù)時(shí)重用這些線程而不是新建一個(gè)線程。線程池中線程的數(shù)量通常完全取決于可用內(nèi)存數(shù)量和應(yīng)用程序的需求。然而,增加可用線程數(shù)量是可能的。線程池中的每個(gè)線程都有被分配一個(gè)任務(wù),一旦任務(wù)已經(jīng)完成了,線程回到池子中并等待下一次分配任務(wù)。

3.線程池作用:

基于以下幾個(gè)原因,在多線程應(yīng)用程序中使用線程池是必須的:

  1. 1.線程池改進(jìn)了一個(gè)應(yīng)用程序的相應(yīng)時(shí)間。由于線程池中的線程已經(jīng)準(zhǔn)備好且等待被分配任務(wù),應(yīng)用程序可以直接拿來(lái)使用而不用新建一個(gè)線程。

  2. 2.線程池節(jié)省了CLR為每個(gè)短生命周期任務(wù)創(chuàng)建一個(gè)完整的線程開(kāi)銷并可以在任務(wù)完成后回收資源。

  3. 3.線程池根據(jù)當(dāng)前在系統(tǒng)中運(yùn)行的進(jìn)程來(lái)優(yōu)化線程時(shí)間片。

  4. 4.線程池允許我們開(kāi)啟多個(gè)任務(wù)而不用為每個(gè)線程設(shè)置屬性。

  5. 5.線程池允許我們?yōu)檎趫?zhí)行任務(wù)的程序參數(shù)傳遞一個(gè)包含狀態(tài)信息的對(duì)象引用。

  6. 6.線程池可以用來(lái)解決處理一個(gè)特定請(qǐng)求最大線程數(shù)量限制問(wèn)題。

4.線程池四種創(chuàng)建方式:

java通過(guò)Executors(jdk1.5的并發(fā)包)提供四種線程池,分別為:

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

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

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

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

總結(jié):newCachedThreadPool 創(chuàng)建的線程,線程池為無(wú)限大,當(dāng)執(zhí)行第二個(gè)任務(wù)時(shí)第一個(gè)任務(wù)已經(jīng)完成,會(huì)復(fù)用執(zhí)行第一個(gè)任務(wù)的線程,而不用每次新建線程。newFixedThreadPool 每次執(zhí)行傳入?yún)?shù)大小個(gè)線程,其他線程在等待(企業(yè)中用的不多)。newScheduledThreadPool 使用schedule方法創(chuàng)建單位時(shí)間的延遲線程池。

代碼實(shí)現(xiàn):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package chauncy.concurrentprogramming.executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewCachedThreadPool {
    public static void main(String[] args) {
        // 創(chuàng)建可緩存線程池
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        // 執(zhí)行execute方法表示創(chuàng)建了一個(gè)線程,類似于start
        for (int i = 0; i < 30; i++) {
            int index = i;
            // index++;
            newCachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                    }
                    // 內(nèi)部類中使用的i必須是final,但是換成index后就不報(bào)錯(cuò),因?yàn)閖dk1.8進(jìn)行了優(yōu)化,能識(shí)別index是否被改變,如果把int
                    // index=i;下邊的index++放開(kāi)就會(huì)報(bào)錯(cuò)。
                    System.out.println(Thread.currentThread().getName() + "----" + index);
                }
            });
        }
        // 關(guān)閉線程池
        newCachedThreadPool.shutdown();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package chauncy.concurrentprogramming.executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewFixedThreadPool {
    public static void main(String[] args) {
        // newFixedThreadPool 每次最多只能執(zhí)行三個(gè),其他線程等待執(zhí)行。
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            int index = i;
            newFixedThreadPool.execute(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                    }
                    System.out.println(Thread.currentThread().getName() + "----i:" + index);
                }
            });
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package chauncy.concurrentprogramming.executors;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class NewScheduledThreadPool {
    public static void main(String[] args) {
        // 入?yún)榫€程池大小,
        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);
        // schedule執(zhí)行定時(shí)任務(wù)線程池,第一個(gè)參數(shù)需要?jiǎng)?chuàng)建Runnable接口對(duì)象,第二、三個(gè)參數(shù)表示多少個(gè)單位時(shí)間執(zhí)行run方法。
        newScheduledThreadPool.schedule(new Runnable() {
            public void run() {
                System.out.println("我是三秒鐘之后執(zhí)行。。。。");
            }
        }, 3, TimeUnit.SECONDS);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package chauncy.concurrentprogramming.executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NewSingleThreadExecutor {
    public static void main(String[] args) {
        ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            int index = i;
            newSingleThreadExecutor.execute(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "----i:" + index);
                }
            });
        }
    }
}

以上所述是小編給大家介紹的Java多線程與并發(fā)編程詳解整合,希望對(duì)大家有所幫助

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多