摘要:前言本篇主要講解如何去優(yōu)化鎖機(jī)制或者克服多線程因?yàn)殒i可導(dǎo)致性能下降的問題線程變量有這樣一個(gè)場(chǎng)景,前面是一大桶水,個(gè)人去喝水,為了保證線程安全,我們要在杯子上加鎖導(dǎo)致大家輪著排隊(duì)喝水,因?yàn)榧恿随i的杯子是同步的,只能有一個(gè)人拿著這個(gè)唯一的杯子喝
前言
本篇主要講解如何去優(yōu)化鎖機(jī)制
或者克服多線程因?yàn)殒i可導(dǎo)致性能下降的問題
有這樣一個(gè)場(chǎng)景,前面是一大桶水,10個(gè)人去喝水,為了保證線程安全,我們要在杯子上加鎖
導(dǎo)致大家輪著排隊(duì)喝水,因?yàn)榧恿随i的杯子是同步的,只能有一個(gè)人拿著這個(gè)唯一的杯子喝水
這樣子大家都喝完一杯水需要很長(zhǎng)的時(shí)間
如果我們給每個(gè)人分發(fā)一個(gè)杯子呢?是不是每人喝到水的時(shí)間縮小到了十分之一
多線程并發(fā)也是一個(gè)道理
在每個(gè)Thread中都有自己的數(shù)據(jù)存放空間(ThreadLocalMap)
而ThreadLocal就是在當(dāng)前線程的存放空間中存放數(shù)據(jù)
下面這個(gè)例子,在每個(gè)線程中存放一個(gè)arraylist,而不是大家去公用一個(gè)arraylist
</>復(fù)制代碼
public class ThreadLocalTest {
public static ThreadLocal threadLocal = new ThreadLocal();
public static ArrayList list = new ArrayList();
public static class Demo implements Runnable {
private int i;
public Demo(int i) {
this.i = i;
}
@Override
public void run() {
list.add(i);
threadLocal.set(list);
System.out.println(threadLocal.get());
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(5);
for (int j = 0; j < 200; j++) {
es.execute(new Demo(j));
}
Thread.sleep(3000);
System.out.println(list.size());
es.shutdown();
}
}
在每個(gè)線程內(nèi)部有一塊存儲(chǔ)區(qū)域叫做ThreadLocalMap
可以看到,ThreadLocal采用set,get存取值方式
只有線程完全關(guān)閉時(shí),在ThreadLocalMap中的數(shù)據(jù)才會(huì)被GC回收
這時(shí)有一個(gè)值得考慮的問題
我們使用線程池來開發(fā)的時(shí)候,線程池中的線程并不會(huì)關(guān)閉,它只是處于空閑狀態(tài)
也就是說,我們?nèi)绻堰^大的數(shù)據(jù)存儲(chǔ)在當(dāng)前線程的ThreadLocalMap中,線程不斷的調(diào)用,被空閑...
最后會(huì)導(dǎo)致內(nèi)存溢出
解決方法是當(dāng)不需要這些數(shù)據(jù)時(shí)
使用ThreadLocal.remove()方法將變量給移除
還有一種脫離鎖的機(jī)制,那就是CAS
CAS帶著三個(gè)變量,分別是:
V更新變量:需要返回的變量
E預(yù)期值:原來的值
N新值,傳進(jìn)來的新變量
只有當(dāng)預(yù)期值和新值相等時(shí),才會(huì)把V=N,如果不相等,說明該操作會(huì)讓數(shù)據(jù)無法同步
根據(jù)上面的解釋,大概就能知道CAS其實(shí)也是在保護(hù)數(shù)據(jù)的同步性
當(dāng)多個(gè)線程進(jìn)行CAS操作時(shí),可想只有一個(gè)線程能成功更新,之后其它線程的E和V會(huì)不地進(jìn)行斷比較
所以CAS的同步鎖的實(shí)現(xiàn)是一樣的
CAS操作的并發(fā)包在Atomic包中,atomic實(shí)現(xiàn)了很多類型
不管是AtomicInteger還是AtomicReference,都有相同點(diǎn),請(qǐng)觀察它們的源碼:
</>復(fù)制代碼
private volatile V value;
private static final long valueOffset;
以上是AtomicReferenc
</>復(fù)制代碼
private volatile int value;
private static final long valueOffset;
以上是AtomicIntege
都有value,這是它們的當(dāng)前實(shí)際值
valueOffset保存的是value的偏移量
下面給出一個(gè)簡(jiǎn)單的AtomicIntege例子:
</>復(fù)制代碼
public class AtomicTest {
public static AtomicInteger atomicInteger = new AtomicInteger();
//public static AtomicReference atomicReference = new AtomicReference();
public static class Demo implements Runnable{
@Override
public void run() {
for (int j=0;j<1000;j++){
atomicInteger.incrementAndGet(); //當(dāng)前值加1并且返回當(dāng)前值
}
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i =0;i<10;i++){
es.submit(new Demo());
}
Thread.sleep(5000);
System.out.println(atomicInteger);
}
}
你試著執(zhí)行一下,如果打印出10000說明線程安全
使用CAS操作比同步鎖擁有更好的性能
我們來看下incrementAndGet()的源碼:
</>復(fù)制代碼
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
來看下getAndAddInt()源碼:
</>復(fù)制代碼
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
這里有一個(gè)循環(huán),再細(xì)看源碼發(fā)現(xiàn)是native的,雖然看不到原生代碼,但是可以看出它這里做了一個(gè)CAS操作,不斷地進(jìn)行多個(gè)變量的比較,只有預(yù)設(shè)值和新值相等時(shí),才跳出循環(huán)
var5就是需要更新的變量,var1和var2是預(yù)設(shè)值和新值
講了那么多無鎖的操作,我們來看一下一個(gè)死鎖的現(xiàn)象
兩個(gè)線程互相占著對(duì)方想得到的鎖,就會(huì)出現(xiàn)死鎖狀況
</>復(fù)制代碼
public class DeadLock extends Thread{
protected String suo;
public static String zuo = new String();
public static String you = new String();
public DeadLock(String suo){
this.suo=suo;
}
@Override
public void run(){
if (suo==zuo){
synchronized (zuo){
System.out.println("拿到了左,正在拿右......");
synchronized (you){
System.out.println("拿到了右,成功了");
}
}
}
if (suo==you){
synchronized (you){
System.out.println("拿到了右,正在拿左......");
synchronized (zuo){
System.out.println("拿到了zuo,成功了");
}
}
}
}
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<10000;i++){
DeadLock t1 = new DeadLock(zuo);
DeadLock t2 = new DeadLock(you);
t1.start();t2.start();
}
Thread.sleep(50000);
}
}
如圖:
出現(xiàn)了兩個(gè)線程的死鎖現(xiàn)象,所以說去鎖不僅能提升性能,也能防止死鎖的產(chǎn)生
今天就先到這里,大家可以看看這些內(nèi)容的拓展
記得點(diǎn)關(guān)注看更新,謝謝閱讀
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/70676.html
摘要:今天就先到這里,大家可以看看這些內(nèi)容的拓展記得點(diǎn)關(guān)注看更新,謝謝閱讀 前言 這是一個(gè)長(zhǎng)篇博客,希望大家關(guān)注我并且一起學(xué)習(xí)java高并發(fā)廢話不多說,直接開始 并行和并發(fā) 并行:多個(gè)線程同時(shí)處理多個(gè)任務(wù)并發(fā):多個(gè)線程處理同個(gè)任務(wù),不一定要同時(shí) 下面用圖來描述并行和并發(fā)的區(qū)別:(實(shí)現(xiàn)和虛線表示兩個(gè)不同的線程) showImg(https://segmentfault.com/img/bVYT...
摘要:可以用代替可以用代替定義的對(duì)象的值是不可變的今天就先到這里,大家可以看看這些內(nèi)容的拓展記得點(diǎn)關(guān)注看更新,謝謝閱讀 前言 java高并發(fā)第二篇講的是java線程的基礎(chǔ)依舊不多說廢話 線程和進(jìn)程 進(jìn)程是操作系統(tǒng)運(yùn)行的基礎(chǔ),是一個(gè)程序運(yùn)行的實(shí)體,windows上打開任務(wù)管理器就能看到進(jìn)程線程是輕量級(jí)的進(jìn)程,是程序執(zhí)行的最小單位,是在進(jìn)程這個(gè)容器下進(jìn)行的 線程基本操作 新建一個(gè)線程類有兩種方式...
摘要:前言今天講的多線程的同步控制直接進(jìn)入正題重入鎖重入鎖可以完全代替,它需要類來實(shí)現(xiàn)下面用一個(gè)簡(jiǎn)單的例子來實(shí)現(xiàn)重入鎖以上代碼打印出來的是,可以說明也實(shí)現(xiàn)了線程同步它相比更加靈活,因?yàn)橹厝腈i實(shí)現(xiàn)了用戶自己加鎖,自己釋放鎖記得一定要釋放,不然其他線 前言 今天講的多線程的同步控制直接進(jìn)入正題 ReentrantLock重入鎖 重入鎖可以完全代替synchronized,它需要java.util...
摘要:前言這篇主要來講解多線程中一個(gè)非常經(jīng)典的設(shè)計(jì)模式包括它的基礎(chǔ)到拓展希望大家能夠有所收獲生產(chǎn)者消費(fèi)者模式簡(jiǎn)述此設(shè)計(jì)模式中主要分兩類線程生產(chǎn)者線程和消費(fèi)者線程生產(chǎn)者提供數(shù)據(jù)和任務(wù)消費(fèi)者處理數(shù)據(jù)和任務(wù)該模式的核心就是數(shù)據(jù)和任務(wù)的交互點(diǎn)共享內(nèi)存緩 前言 這篇主要來講解多線程中一個(gè)非常經(jīng)典的設(shè)計(jì)模式包括它的基礎(chǔ)到拓展希望大家能夠有所收獲 生產(chǎn)者-消費(fèi)者模式簡(jiǎn)述 此設(shè)計(jì)模式中主要分兩類線程:生產(chǎn)者...
摘要:只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙只有動(dòng)手,你才能真正掌握一門技術(shù)持續(xù)更新中項(xiàng)目地址求求求源碼系列跟一起學(xué)如何寫函數(shù)庫中高級(jí)前端面試手寫代碼無敵秘籍如何用不到行代碼寫一款屬于自己的類庫原理講解實(shí)現(xiàn)一個(gè)對(duì)象遵循規(guī)范實(shí)戰(zhàn)手摸手,帶你用擼 Do it yourself!!! 只有動(dòng)手,你才能真的理解作者的構(gòu)思的巧妙 只有動(dòng)手,你才能真正掌握一門技術(shù) 持續(xù)更新中…… 項(xiàng)目地址 https...
閱讀 1977·2021-11-24 10:45
閱讀 1465·2021-11-18 13:15
閱讀 4557·2021-09-22 15:47
閱讀 3935·2021-09-09 11:36
閱讀 2017·2019-08-30 15:44
閱讀 3096·2019-08-29 13:05
閱讀 2508·2019-08-29 12:54
閱讀 2001·2019-08-26 13:47
极致性价比!云服务器续费无忧!
Tesla A100/A800、Tesla V100S等多种GPU云主机特惠2折起,不限台数,续费同价。
NVIDIA RTX 40系,高性价比推理显卡,满足AI应用场景需要。
乌兰察布+上海青浦,满足东推西训AI场景需要