欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2019年03月23日 10:35:30   作者:努力的程序員先生  
這篇文章主要介紹了Java多線程與并發(fā)編程,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、多線程三大特性

多線程有三大特性:原子性、可見性、有序性。

原子性

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

可見性:

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

有序性:

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

例如:

int a = 10; //語句1
int r = 2; //語句2
a = a + 3; //語句3
r = a*a;  //語句4

因為重排序,他還可能執(zhí)行順序為 2-1-3-4,1-3-2-4
但絕不可能 2-1-4-3,因為這打破了依賴關(guān)系。
顯然重排序?qū)尉€程運行是不會有任何問題,而多線程就不一定了,所以我們在多線程編程時就得考慮這個問題了。
多線程中保證有序性的方法: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)存模型(簡稱JMM),JMM決定一個線程對共享變量的寫入時,能對另一個線程可見。從抽象的角度來看,JMM定義了線程和主內(nèi)存之間的抽象關(guān)系:線程之間的共享變量存儲在主內(nèi)存(main memory)中(局部變量不會存儲在),每個線程都有一個私有的本地內(nèi)存(local memory),本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本。本地內(nèi)存是JMM的一個抽象概念,并不真實存在。它涵蓋了緩存、寫緩沖區(qū)、寄存器以及其他的硬件和編輯器優(yōu)化。

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

三、Volatile關(guān)鍵字

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

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

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

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

  1. 1.volatile輕量級,只能修飾變量。synchronized重量級,還可修飾方法。
  2. 2.volatile只能保證數(shù)據(jù)的可見性,不能用來同步,因為多個線程并發(fā)訪問volatile修飾的變量不會阻塞。

synchronized不僅保證可見性,而且還保證原子性,因為只有獲得了鎖的線程才能進入臨界區(qū),從而保證臨界區(qū)中的所有語句都全部執(zhí)行。多個線程爭搶synchronized鎖對象時會出現(xiàn)阻塞。

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

線程安全性包括兩個方便:1.可見性 2.原子性

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

代碼實現(xiàn):

package chauncy.concurrentprogramming;

class ThreadVolatile extends Thread {
	public volatile boolean flag = true;

	@Override
	public void run() {
		System.out.println("子線程開始執(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);
		/**
		 * 如果不對變量加Volatile關(guān)鍵字,則子線程不會停止運行 原因:線程之間是不可見的,讀取的是副本,沒有及時讀取到主內(nèi)存結(jié)果。
		 * 解決辦法:使用Volatile關(guān)鍵字解決線程之間的可見性,強制線程每次讀取該值的時候都去“主內(nèi)存”中取值。
		 */
		threadVolatile1.isRun(false);
		System.out.println("flag:" + threadVolatile1.flag);
	}
}
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修飾不具有原子性(不具有同步性),不能解決線程安全問題)
 * @author: ChauncyWang
 * @createTime: 2019年3月12日 上午10:39:30
 * @version: 1.0
 */
public class VolatileNoAtomic {
	public static void main(String[] args) {
		// 初始化10個線程
		VolatileNoAtomicThread[] volatileNoAtomicThread = new VolatileNoAtomicThread[10];
		for (int i = 0; i < volatileNoAtomicThread.length; i++) {
			// 創(chuàng)建每一個線程
			volatileNoAtomicThread[i] = new VolatileNoAtomicThread();
		}
		for (int i = 0; i < volatileNoAtomicThread.length; i++) {
			// 啟動每一個線程
			volatileNoAtomicThread[i].start();
		}
	}
}

四、TreadLocal

1.什么是ThreadLocal?

ThreadLocal提高一個線程的局部變量,訪問某個線程擁有自己局部變量。

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

ThreadLocal接口方法有4個:

  1. void set(Object value)設(shè)置當(dāng)前線程的線程局部變量的值;
  2. public Object get()該方法返回當(dāng)前線程所對應(yīng)的線程局部變量;
  3. public void remove()將當(dāng)前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK5.0新增的方法。需要指出的是,當(dāng)線程結(jié)束后,對應(yīng)該線程的局部變量將自動被垃圾回收,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,但它可以加快內(nèi)存的回收速度;
  4. protected Object initialValue()返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設(shè)計的。這個方法是一個延遲調(diào)用方法,在線程第1次調(diào)用get()或set(Object)時才執(zhí)行,并且僅執(zhí)行1次。ThreadLocal中的缺省實現(xiàn)直接返回一個null。

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

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

操作map集合:ThreadLocalMap

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

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

代碼實現(xiàn):

package chauncy.concurrentprogramming;

class Res {
	// private int count=0;
	/*
	 * 設(shè)置本地局部變量,和其他線程局部變量隔離開,互不影響
	 */
	private ThreadLocal<Integer> count = new ThreadLocal<Integer>() {
		protected Integer initialValue() {
			// 設(shè)置當(dāng)前線程局部變量的初始化值
			return 0;
		};
	};

	/**
	 * 
	 * @methodDesc: 功能描述(生成訂單號)
	 * @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)建三個線程,每個線程生成自己獨立的序列號)
 * @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.為什么要使用線程池?

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

2.什么是線程池?

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

3.線程池作用:

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

  1. 1.線程池改進了一個應(yīng)用程序的相應(yīng)時間。由于線程池中的線程已經(jīng)準(zhǔn)備好且等待被分配任務(wù),應(yīng)用程序可以直接拿來使用而不用新建一個線程。
  2. 2.線程池節(jié)省了CLR為每個短生命周期任務(wù)創(chuàng)建一個完整的線程開銷并可以在任務(wù)完成后回收資源。
  3. 3.線程池根據(jù)當(dāng)前在系統(tǒng)中運行的進程來優(yōu)化線程時間片。
  4. 4.線程池允許我們開啟多個任務(wù)而不用為每個線程設(shè)置屬性。
  5. 5.線程池允許我們?yōu)檎趫?zhí)行任務(wù)的程序參數(shù)傳遞一個包含狀態(tài)信息的對象引用。
  6. 6.線程池可以用來解決處理一個特定請求最大線程數(shù)量限制問題。

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

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

  1. 1.newCachedThreadPool 創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
  2. 2.newFixedThreadPool 創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù),超出的線程會在隊列中等待。
  3. 3.newScheduledThreadPool 創(chuàng)建一個定長線程池,支持定時及周期性任務(wù)執(zhí)行
  4. 4.newSingleThreadExecutor 創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務(wù),保證所有任務(wù)按照指定順序(FIFO,LIFO,優(yōu)先級)執(zhí)行。(一般不會使用)

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

代碼實現(xiàn):

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)建了一個線程,類似于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后就不報錯,因為jdk1.8進行了優(yōu)化,能識別index是否被改變,如果把int
					// index=i;下邊的index++放開就會報錯。
					System.out.println(Thread.currentThread().getName() + "----" + index);
				}
			});
		}
		// 關(guān)閉線程池
		newCachedThreadPool.shutdown();
	}
}
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í)行三個,其他線程等待執(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);
				}
			});
		}
	}
}
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í)行定時任務(wù)線程池,第一個參數(shù)需要創(chuàng)建Runnable接口對象,第二、三個參數(shù)表示多少個單位時間執(zhí)行run方法。
		newScheduledThreadPool.schedule(new Runnable() {
			public void run() {
				System.out.println("我是三秒鐘之后執(zhí)行。。。。");
			}
		}, 3, TimeUnit.SECONDS);
	}
}
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ā)編程詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Java面試題之基本語法(圖解)

    Java面試題之基本語法(圖解)

    這篇文章主要介紹了關(guān)于Java面試題之基本語法的相關(guān)資料,文中通過圖片說明介紹的很詳細,相信對大家具有一定的參考價值,有需要的朋友們下面來一起看看吧。
    2017-02-02
  • Netty分布式FastThreadLocal的set方法實現(xiàn)邏輯剖析

    Netty分布式FastThreadLocal的set方法實現(xiàn)邏輯剖析

    這篇文章主要為大家介紹了Netty分布式FastThreadLocal的set方法實現(xiàn)邏輯剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-03-03
  • Spring boot 打jar包分離lib的正確配置方式

    Spring boot 打jar包分離lib的正確配置方式

    spring boot打jar包分離lib后,配置文件的方式,在網(wǎng)上可以搜到很多答案,但是都不夠完善,今天小編給大家?guī)砹薙pring boot 打jar包分離lib的正確配置方式,感興趣的朋友一起看看吧
    2018-02-02
  • 淺談Storm在zookeeper上的目錄結(jié)構(gòu)

    淺談Storm在zookeeper上的目錄結(jié)構(gòu)

    這篇文章主要介紹了淺談Storm在zookeeper上的目錄結(jié)構(gòu)的相關(guān)內(nèi)容,涉及storm使用zookeeper的操作以及詳細結(jié)構(gòu)圖,具有一定參考價值,需要的朋友可以了解下。
    2017-10-10
  • Spring Boot 如何整合連接池

    Spring Boot 如何整合連接池

    這篇文章主要介紹了Spring Boot 如何整合連接池,幫助大家更好的理解和學(xué)習(xí)spring boot框架,感興趣的朋友可以了解下
    2020-11-11
  • 詳解使用Spring Security OAuth 實現(xiàn)OAuth 2.0 授權(quán)

    詳解使用Spring Security OAuth 實現(xiàn)OAuth 2.0 授權(quán)

    本篇文章主要介紹了詳解使用Spring Security OAuth 實現(xiàn)OAuth 2.0 授權(quán),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-01-01
  • Windows下如何安裝配置Redis環(huán)境

    Windows下如何安裝配置Redis環(huán)境

    這篇文章主要介紹了Windows下如何安裝配置Redis環(huán)境,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-12-12
  • sqlserver和java將resultSet中的記錄轉(zhuǎn)換為學(xué)生對象

    sqlserver和java將resultSet中的記錄轉(zhuǎn)換為學(xué)生對象

    這篇文章主要介紹了如何利用sqlserver和java將resultSet中的記錄轉(zhuǎn)換為學(xué)生對象,附有超詳細的代碼,需要的朋友可以參考一下,希望對你有所幫助
    2021-12-12
  • 優(yōu)雅地在Java應(yīng)用中實現(xiàn)全局枚舉處理的方法

    優(yōu)雅地在Java應(yīng)用中實現(xiàn)全局枚舉處理的方法

    這篇文章主要給大家介紹了關(guān)于如何優(yōu)雅地在Java應(yīng)用中實現(xiàn)全局枚舉處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • java接收ios文件上傳的示例代碼

    java接收ios文件上傳的示例代碼

    這篇文章主要為大家詳細介紹了java接收ios文件上傳的示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05

最新評論