Java Atomic類及線程同步新機(jī)制原理解析
一、為什么要使用Atomic類?
看一下下面這個小程序,模擬計(jì)數(shù),創(chuàng)建10個線程,共同訪問這個int count = 0 ;每個線程給count往上加10000,這個時候你需要加鎖,如果不加鎖會出現(xiàn)線程安全問題,但是使用AtomicInteger之后就不用再做加鎖的操作了,因?yàn)锳tomicInteger內(nèi)部使用了CAS操作,直接無鎖往上遞增,有人會問問什么會出現(xiàn)無鎖操作,答案只有一個:那就是快唄;
下面是AtomicInteger的使用方法:
package com.example.demo.threaddemo.juc_008;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author D-L
* @Classname T01_AtomicInteger
* @Version 1.0
* @Description 使用AtomicInteger類代替synchronized
* @Date 2020/7/22
*/
public class T01_AtomicInteger {
// int count = 0;
AtomicInteger count = new AtomicInteger(0);
public /**synchronized*/ void m(){
for (int i = 0; i < 10000; i++) {
// count++;
count.incrementAndGet();
}
}
public static void main(String[] args) {
T01_AtomicInteger t = new T01_AtomicInteger();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(t::m ,"Thread" + i));
}
threads.forEach(o -> o.start());
threads.forEach(o ->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
/* for (int i = 0; i < 10; i++) {
new Thread(t::m ,"Thread"+i).start();
}
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println(t.count);
}
}
二、Atomic類,synchronized、LongAdder的效率驗(yàn)證 及 分析
模擬多個線程對一個數(shù)進(jìn)行遞增,多線程對一個共享變量進(jìn)行遞增的方法大概有三種;驗(yàn)證一下它們的效率,這里做一些粗糙的測試,基本已經(jīng)能說明問題,具體情況還要根據(jù)實(shí)際情況:
第一種:使用long count = 0; 加鎖來實(shí)現(xiàn);
第二種:使用AtomicLong類來實(shí)現(xiàn);
第三種:使用LongAdder實(shí)現(xiàn);
package com.example.demo.threaddemo.juc_008;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
/**
* @author D-L
* @Classname T02_AtomicVsSyncVsLongAdder
* @Version 1.0
* @Description 測試Atomic類 synchronized LongAdder效率
* @Date 2020/7/22
*/
public class T02_AtomicVsSyncVsLongAdder {
static AtomicLong count1 = new AtomicLong(0L);
static Long count2 = 0L;
static LongAdder count3 = new LongAdder();
public static void main(String[] args) throws InterruptedException {
Thread [] threads = new Thread[1000];
/*-----------------------------------Atomic類-----------------------------------*/
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() ->{
for (int j = 0; j < 100000; j++) {
count1.incrementAndGet();
}
});
}
long start = System.currentTimeMillis();
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
long end = System.currentTimeMillis();
System.out.println("Atomic:" + count1.get() +"-----time:" +(end - start));
/*----------------------------------synchronized---------------------------------*/
Object lock = new Object();
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 100000; j++) {
synchronized (lock) {
count2++;
}
}
}
});
}
long start2 = System.currentTimeMillis();
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
long end2 = System.currentTimeMillis();
System.out.println("synchronized:" + count1.get() +"-----time:" +(end2 - start2));
/*-------------------------------------LongAdder----------------------------------*/
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() ->{
for (int j = 0; j < 100000; j++) {
count3.increment();
}
});
}
long start3 = System.currentTimeMillis();
for (Thread t : threads) t.start();
for (Thread t : threads) t.join();
long end3 = System.currentTimeMillis();
System.out.println("LongAdder:" + count1.get() +"-----time:" +(end3 - start3));
}
}
/*----------------------------------運(yùn)行結(jié)果---------------------------------*/
Atomic:100000000-----time:2096synchronized:100000000-----time:5765LongAdder:100000000-----time:515
從以上的結(jié)果來看并發(fā)量達(dá)到一定程度運(yùn)行效率:LongAdder > AtomicLong > synchronized; 這個還只是一個粗略的測試,具體使用還要根據(jù)實(shí)際情況。
為什么AtomicLong的效率比synchronized的效率高?
AtomicLong的底層使用的是CAS操作(無鎖優(yōu)化),而synchronized雖然底層做了優(yōu)化但是并發(fā)量達(dá)到一定層度,存在鎖的膨脹,最終會變成重量級鎖,需要向操作系統(tǒng)申請鎖資源,所以synchronized的效率慢一點(diǎn)合情合理。
為什么LongAdder的效率比AtomicLong的效率高?
因?yàn)長ongAdder使用了分段鎖的概念,效率比AtomicLong的效率高。

分段鎖的意思就是用一個數(shù)組把線程分成若干組,然后運(yùn)行結(jié)束后把結(jié)果累加起來,例如你有1000個線程,數(shù)組的長度為4,那就把0-250個放到數(shù)組的第0位,以此類推,然后把四個數(shù)組中線程的計(jì)算結(jié)果累加,這樣會很大程度上節(jié)省時間,從而提高效率。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Spring實(shí)現(xiàn)類私有方法的幾個問題(親測通用解決方案)
現(xiàn)實(shí)的業(yè)務(wù)場景中,可能需要對Spring的實(shí)現(xiàn)類的私有方法進(jìn)行測試。本文給大家分享Spring實(shí)現(xiàn)類私有方法面臨的幾個問題及解決方案,感興趣的朋友跟隨小編一起看看吧2021-06-06
Mybatis Criteria使用and和or進(jìn)行聯(lián)合條件查詢的操作方法
這篇文章主要介紹了Mybatis Criteria的and和or進(jìn)行聯(lián)合條件查詢的方法,本文通過例子給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2021-10-10
Mybatis整合達(dá)夢數(shù)據(jù)庫的完整步驟記錄
作為國產(chǎn)數(shù)據(jù)庫,達(dá)夢做的不錯,提供的遷移工具也相當(dāng)不錯,下面這篇文章主要給大家介紹了關(guān)于Mybatis整合達(dá)夢數(shù)據(jù)庫的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
springboot?maven?打包插件介紹及注意事項(xiàng)說明
這篇文章主要介紹了springboot?maven?打包插件介紹及注意事項(xiàng)說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
Java中==與equals()及hashcode()三者之間的關(guān)系詳解
最近也是在讀Hollis的《深入理解Java核心技術(shù)》里面一節(jié)講到了equals()和hashcode()的關(guān)系,對于這個高頻面試點(diǎn),咱們需要認(rèn)真理清一下幾者之間的關(guān)系2022-10-10
Dubbo無法訪問遠(yuǎn)程Zookeeper已注冊服務(wù)的問題解決方案
今天小編就為大家分享一篇關(guān)于Dubbo無法訪問遠(yuǎn)程Zookeeper已注冊服務(wù)的問題解決方案,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03

