Java Atomic類及線程同步新機(jī)制原理解析
一、為什么要使用Atomic類?
看一下下面這個(gè)小程序,模擬計(jì)數(shù),創(chuàng)建10個(gè)線程,共同訪問這個(gè)int count = 0 ;每個(gè)線程給count往上加10000,這個(gè)時(shí)候你需要加鎖,如果不加鎖會(huì)出現(xiàn)線程安全問題,但是使用AtomicInteger之后就不用再做加鎖的操作了,因?yàn)锳tomicInteger內(nèi)部使用了CAS操作,直接無鎖往上遞增,有人會(huì)問問什么會(huì)出現(xiàn)無鎖操作,答案只有一個(gè):那就是快唄;
下面是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)證 及 分析
模擬多個(gè)線程對(duì)一個(gè)數(shù)進(jìn)行遞增,多線程對(duì)一個(gè)共享變量進(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; 這個(gè)還只是一個(gè)粗略的測試,具體使用還要根據(jù)實(shí)際情況。
為什么AtomicLong的效率比synchronized的效率高?
AtomicLong的底層使用的是CAS操作(無鎖優(yōu)化),而synchronized雖然底層做了優(yōu)化但是并發(fā)量達(dá)到一定層度,存在鎖的膨脹,最終會(huì)變成重量級(jí)鎖,需要向操作系統(tǒng)申請(qǐng)鎖資源,所以synchronized的效率慢一點(diǎn)合情合理。
為什么LongAdder的效率比AtomicLong的效率高?
因?yàn)長ongAdder使用了分段鎖的概念,效率比AtomicLong的效率高。

分段鎖的意思就是用一個(gè)數(shù)組把線程分成若干組,然后運(yùn)行結(jié)束后把結(jié)果累加起來,例如你有1000個(gè)線程,數(shù)組的長度為4,那就把0-250個(gè)放到數(shù)組的第0位,以此類推,然后把四個(gè)數(shù)組中線程的計(jì)算結(jié)果累加,這樣會(huì)很大程度上節(jié)省時(shí)間,從而提高效率。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Bean?Searcher配合SpringBoot的使用詳解
這篇文章主要介紹了Bean?Searcher配合SpringBoot的使用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
HotSpot的Java對(duì)象模型之Oop-Klass模型詳解
這篇文章主要介紹了HotSpot的Java對(duì)象模型之Oop-Klass模型詳解,在JVM層面,不僅Java類是對(duì)象,Java 方法也是對(duì)象, 字節(jié)碼常量池也是對(duì)象,一切皆是對(duì)象,JVM使用不同的oop-klass模型來表示各種不同的對(duì)象,需要的朋友可以參考下2023-08-08
詳解java調(diào)用存儲(chǔ)過程并封裝成map
這篇文章主要介紹了詳解java調(diào)用存儲(chǔ)過程并封裝成map的相關(guān)資料,希望通過本文能幫助到大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-09-09
Springboot項(xiàng)目因?yàn)閗ackson版本問題啟動(dòng)報(bào)錯(cuò)解決方案
這篇文章主要介紹了Springboot項(xiàng)目因?yàn)閗ackson版本問題啟動(dòng)報(bào)錯(cuò)解決方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
解決Springboot-application.properties中文亂碼問題
這篇文章主要介紹了解決Springboot-application.properties中文亂碼問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
SpringBootTest測試時(shí)不啟動(dòng)程序的問題
這篇文章主要介紹了SpringBootTest測試時(shí)不啟動(dòng)程序的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
java控制臺(tái)實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)(IO版)
這篇文章主要為大家詳細(xì)介紹了java控制臺(tái)實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)(IO版),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04
Java編程實(shí)現(xiàn)NBA賽事接口調(diào)用實(shí)例代碼
這篇文章主要介紹了Java編程實(shí)現(xiàn)NBA賽事接口調(diào)用實(shí)例代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11

