原子性 atomic 類用法

當程序更新一個變量時,如果多線程同時更新這個變量,可能得到期望之外的值,比如變量i=1,A線程更新i+1,B線程也更新i+1,經過兩個線程操作之后可能i不等于3,而是等于2。因為A和B線程在更新變量i的時候拿到的i都是1,這就是線程不安全的更新操作,通常我們會使用synchronized來解決這個問題,synchronized會保證多線程不會同時更新變量i。

?

 
  1. import java.util.concurrent.CountDownLatch;public class UnSafeAdd {private static int threadCount=10;private static CountDownLatch countDown=new CountDownLatch(threadCount);private static int count=0;private static class Counter implements Runnable{@Overridepublic void run() {for(int i=0;i<1000;i++){count++;//非原子操作}countDown.countDown();}}public static void main(String[] args) throws InterruptedException {Thread[] threads=new Thread[threadCount];for(int i=0;i<threadCount;i++){threads[i]=new Thread(new Counter());}for(int i=0;i<threadCount;i++){threads[i].start();;}countDown.await();System.out.println(count);}

    ?

輸出:

?

8968

?

 

  1. ?

    import java.util.concurrent.CountDownLatch;public class SafeAddWithSyn {private static int threadCount=10;private static CountDownLatch countDown=new CountDownLatch(threadCount);private static int count=0;synchronized private static void addCount(){//同步方法count++;}private static class Counter implements Runnable{@Overridepublic void run() {for(int i=0;i<1000;i++){addCount();}countDown.countDown();}}public static void main(String[] args) throws InterruptedException {Thread[] threads=new Thread[threadCount];for(int i=0;i<threadCount;i++){threads[i]=new Thread(new Counter());}for(int i=0;i<threadCount;i++){threads[i].start();;}countDown.await();System.out.println(count);}}

    ?

輸出:

?

10000

而Java從JDK 1.5開始提供了java.util.concurrent.atomic包(以下簡稱Atomic包),這個包中的原子操作類提供了一種用法簡單、性能高效、線程安全地更新一個變量的方式。因為變量的類型有很多種,所以在Atomic包里一共提供了13個類,屬于4種類型的原子更新方式,分別是原子更新基本類型、原子更新數組、原子更新引用和原子更新屬性(字段)。Atomic包里的類基本都是使用Unsafe實現的包裝類。

?

 
  1. import java.util.concurrent.CountDownLatch;import java.util.concurrent.atomic.AtomicInteger;public class SafeAddWithAtomicInteger {private static int threadCount=10;private static CountDownLatch countDown=new CountDownLatch(threadCount);private static AtomicInteger count=new AtomicInteger(0);//原子操作類private static class Counter implements Runnable{@Overridepublic void run() {for(int i=0;i<1000;i++){count.addAndGet(1);}countDown.countDown();}}public static void main(String[] args) throws InterruptedException {Thread[] threads=new Thread[threadCount];for(int i=0;i<threadCount;i++){threads[i]=new Thread(new Counter());}for(int i=0;i<threadCount;i++){threads[i].start();;}countDown.await();System.out.println(count.get());}}

    ?

  2. ?

輸出:

?

10000

原子更新基本類型

使用原子的方式更新基本類型,Atomic包提供了以下3個類。
AtomicBoolean:原子更新布爾類型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新長整型。
以上3個類提供的方法幾乎一模一樣,所以本節僅以AtomicInteger為例進行講解,AtomicInteger的源碼如下:

?

 
  1. public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// setup to use Unsafe.compareAndSwapInt for updates 使用Unsafe類的CAS操作實現更新private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;......//volatile保證可見性private volatile int value;//構造器public AtomicInteger(int initialValue) {value = initialValue;}public AtomicInteger() {}......//CAS更新public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}....../*以下方法都使用循環CAS更新數據*/<pre name="code" class="java"> public final int getAndSet(int newValue) {//以原子方式設置新值for (;;) {int current = get();if (compareAndSet(current, newValue))return current;}}

    ?

  2. ?

?public final int getAndIncrement() {//以原子方式自增 for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } ......
//以原子方式將輸入值與當前值相加并返回結果 public final int addAndGet(int delta) { for (;;) {//循環 int current = get(); int next = current + delta; if (compareAndSet(current, next))//CAS return next; } } ......}

?

 

?

原子更新數組

通過原子的方式更新數組里的某個元素,Atomic包提供了以3類
AtomicIntegerArray:原子更新整型數組里的元素。
AtomicLongArray:原子更新長整型數組里的元素。
AtomicReferenceArray:原子更新引用類型數組里的元素。

?

?

import java.util.concurrent.CountDownLatch;public class AtomicIntegerArrayTest {private static int threadCount=1000;private static CountDownLatch countDown=new CountDownLatch(threadCount);static int[] values=new int[10];private static class Counter implements Runnable{@Overridepublic void run() {for(int i=0;i<100;i++){for(int j=0;j<10;j++){//所有元素+1values[j]++;}}countDown.countDown();}}public static void main(String[] args) throws InterruptedException{Thread[] threads=new Thread[threadCount];for(int i=0;i<threadCount;i++){threads[i]=new Thread(new Counter());}for(int i=0;i<threadCount;i++){threads[i].start();;}countDown.await();for(int i=0;i<10;i++){System.out.print(values[i]+" ");}}}
  1. ?

輸出:

?

99997 99996 99997 99997 99996 99996 99996 99996 99996 99996

 
  1. import java.util.concurrent.CountDownLatch;import java.util.concurrent.atomic.AtomicIntegerArray;public class AtomicIntegerArrayTest {private static int threadCount=1000;private static CountDownLatch countDown=new CountDownLatch(threadCount);static int[] values=new int[10];static AtomicIntegerArray ai=new AtomicIntegerArray(values);private static class Counter implements Runnable{@Overridepublic void run() {for(int i=0;i<100;i++){for(int j=0;j<10;j++){//所有元素+1ai.getAndIncrement(j);}}countDown.countDown();}}public static void main(String[] args) throws InterruptedException{Thread[] threads=new Thread[threadCount];for(int i=0;i<threadCount;i++){threads[i]=new Thread(new Counter());}for(int i=0;i<threadCount;i++){threads[i].start();;}countDown.await();for(int i=0;i<10;i++){System.out.print(ai.get(i)+" ");}System.out.println();for(int i=0;i<10;i++){System.out.print(values[i]+" ");}}}

    ?

  2. ?

輸出:

?

100000 100000 100000 100000 100000 100000 100000 100000 100000 100000
0 0 0 0 0 0 0 0 0 0
需要注意的是,數組value通過構造方法傳遞進去,然后AtomicIntegerArray會將當前數組復制一份,所以當AtomicIntegerArray對內部的數組元素進行修改時,不會影響傳入的數組。

?

 
  1. //An {@code int} array in which elements may be updated atomically.public class AtomicIntegerArray implements java.io.Serializable {......private final int[] array;//存儲數據的int數組......//構造器public AtomicIntegerArray(int length) {array = new int[length];}public AtomicIntegerArray(int[] array) {// Visibility guaranteed by final field guaranteesthis.array = array.clone();//這里會復制傳入的int數組,因此不會改變原來的int數組}public final int length() {return array.length;}......}

    ?

  2. ?

?

原子更新引用

原子更新基本類型的AtomicInteger,只能更新一個變量,如果要原子更新多個變量,就需要使用這個原子更新引用類型提供的類。Atomic包提供了以下3個類。
AtomicReference:原子更新引用類型。
AtomicReferenceFieldUpdater:原子更新引用類型里的字段。
AtomicMarkableReference:原子更新帶有標記位的引用類型。

?

 
  1. import java.util.concurrent.CountDownLatch;import java.util.concurrent.atomic.AtomicReference;public class Test {private static int threadCount=10;private static CountDownLatch countDown=new CountDownLatch(threadCount);public static AtomicReference<User> atomicUserRef = new AtomicReference<User>();private static class ReferenceUpdater implements Runnable{User user;public ReferenceUpdater(User user){this.user=user;}@Overridepublic void run() {for(int i=0;i<1000;i++){User oldUser=atomicUserRef.get();atomicUserRef.compareAndSet(oldUser, user);Thread.yield();}countDown.countDown();}}public static void main(String[] args) throws InterruptedException {Thread[] threads=new Thread[threadCount];for(int i=0;i<threadCount;i++){threads[i]=new Thread(new ReferenceUpdater(new User("name"+i,i)));}for(int i=0;i<threadCount;i++){threads[i].start();;}countDown.await();System.out.println(atomicUserRef.get().getName());System.out.println(atomicUserRef.get().getOld());}static class User {private String name;private int old;public User(String name, int old) {this.name = name;this.old = old;}public String getName() {return name;}public int getOld() {return old;}}}

    ?

  2. ?

?

輸出:

name1
1
每次輸出結果都不確定,10種情況都有可能,但是name屬性和age屬性是匹配的。

?

 
  1. /**An object reference that may be updated atomically.*/public class AtomicReference<V> implements java.io.Serializable {<pre name="code" class="java"> ......

    ?

private static final Unsafe unsafe = Unsafe.getUnsafe();//用于CAS更新 ...... private volatile V value;//引用,volatile保證可見性 public AtomicReference(V initialValue) { value = initialValue; } public AtomicReference() { } //利用Unsafe類執行CAS操作 public final boolean compareAndSet(V expect, V update) { return unsafe.compareAndSwapObject(this, valueOffset, expect, update); } ......
//循環CAS public final V getAndSet(V newValue) { while (true) { V x = get(); if (compareAndSet(x, newValue)) return x; } }
......}

?

 

?

原子更新字段

如果需原子地更新某個類里的某個字段時,就需要使用原子更新字段類,Atomic包提供了以下3個類進行原子字段更新。
AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
AtomicLongFieldUpdater:原子更新長整型字段的更新器。
AtomicStampedReference:原子更新帶有版本號的引用類型。該類將整數值與引用關聯起來,可用于原子的更新數據和數據的版本號,可以解決使用CAS進行原子更新時可能出現的ABA問題。

要想原子地更新字段類需要兩步。第一步,因為原子更新字段類都是抽象類,每次使用的時候必須使用靜態方法newUpdater()創建一個更新器,并且需要設置想要更新的類和屬性。第二步,更新類的字段(屬性)必須使用public volatile修飾符。

?

 
  1. import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;public class AtomicIntegerFieldUpdaterTest {// 創建原子更新器,并設置需要更新的對象類和對象的屬性private static AtomicIntegerFieldUpdater<User> a =AtomicIntegerFieldUpdater.newUpdater(User.class, "old");public static void main(String[] args) throws InterruptedException {// 設置柯南的年齡是10歲User conan = new User("conan", 10);// 柯南長了一歲,但是仍然會輸出舊的年齡System.out.println(a.getAndIncrement(conan));// 輸出柯南現在的年齡System.out.println(a.get(conan));}public static class User {private String name;public volatile int old;public User(String name, int old) {this.name = name;this.old = old;}public String getName() {return name;}public int getOld() {return old;}}}

    ?

  2. ?

輸出:

?

10

11

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/386497.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386497.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386497.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

這是一份用心整理的Android面試總結,聰明人已經收藏了!

前言 本文想分享的是如何準備阿里面試的以及面試過程的所想所得&#xff0c;希望能幫到你。 首先&#xff0c;可能要讓你們失望的是&#xff0c;這篇文章不會有大篇幅的面試題答案。如果想要看這方面的內容&#xff0c;可以看我之前的文章。感謝關注 很多人準備面試的時候&a…

git 技能圖

---- 轉載于:https://www.cnblogs.com/WHWWHW/p/11136606.html

AtomicStampedReference源碼分析

之前的文章已經介紹過CAS的操作原理&#xff0c;它雖然能夠保證數據的原子性&#xff0c;但還是會有一個ABA的問題。 那么什么是ABA的問題呢&#xff1f;假設有一個共享變量“num”,有個線程A在第一次進行修改的時候把num的值修改成了33。修改成功之后&#xff0c;緊接著又立刻…

django:bootstrap table加載django返回的數據

bootstrap table加載表格數據有兩類方式&#xff1a; 一種通過data屬性的方式配置&#xff0c;一種是javascipt方式配置 這里看js配置方式&#xff1a; 1、當數據源為.json文件時 url參數寫上json文件的地址就行&#xff0c;但是json文件格式必須為json格式(2種): a:一種為json…

這是一份面向Android開發者的復習指南,成功入職字節跳動

前言 19年6月份從網易云音樂離開&#xff0c;放棄了留學機會&#xff0c;開始了人生的第一次創業&#xff0c;前后嘗試了兩個項目&#xff0c;因為個人能力與時機因素都失敗了&#xff0c;雖然沒能享受到創業所能夠帶來高杠桿物質上的回報&#xff0c;但是對個人軟技能和自我邊…

JVM啟動參數

不管是YGC還是Full GC,GC過程中都會對導致程序運行中中斷,正確的選擇不同的GC策略,調整JVM、GC的參數&#xff0c;可以極大的減少由于GC工作&#xff0c;而導致的程序運行中斷方面的問題&#xff0c;進而適當的提高Java程序的工作效率。但是調整GC是以個極為復雜的過程&#xf…

【UOJ 92】有向圖的強連通分量

【題目描述】&#xff1a; 有向圖強連通分量&#xff1a;在有向圖G中&#xff0c;如果兩個頂點vi,vj間&#xff08;vi>vj&#xff09;有一條從vi到vj的有向路徑&#xff0c;同時還有一條從vj到vi的有向路徑&#xff0c;則稱兩個頂點強連通(strongly connected)。如果有向圖G…

這篇文章可以滿足你80%日常工作!一線互聯網公司面經總結

前言 最近發現大家都喜歡看面試相關的文章&#xff0c;我也跟一波風&#xff0c;總結了一下我面試中所遇到的問題總結&#xff0c;分享一下面試中被問的最多的一些問題。 希望對正在找工作的朋友提供一些幫助。 好了話不多說&#xff0c;進入正題。 作為安卓開發者&#xff…

java并發synchronized 鎖的膨脹過程(鎖的升級過程)深入剖析(1)

我們先來說一下我們為什么需要鎖&#xff1f; 因為在并發情況為了保證線程的安全性&#xff0c;是在一個多線程環境下正確性的概念&#xff0c;也就是保證多線程環境下共享的、可修改的狀態的正確性&#xff08;這里的狀態指的是程序里的數據&#xff09;&#xff0c;在java程…

MSCRM二次開發實現自動編號功能

功能描述&#xff1a;對客戶實體實現自動編號功能&#xff0c;1、2、3、4...... 自動編號存放于屬性accountnumber.原  理&#xff1a;在mscrm服務器用一個文本文件存放當前最新編號&#xff0c;每當創建客戶記錄時在PreCreate事件接口做以下步驟&#xff1a;1、鎖定文本文件…

這篇文章可以滿足你80%日常工作!成功入職騰訊

什么是中年危機 根據權威數據顯示&#xff0c;國內IT程序員鼎盛時期是在25-27歲左右&#xff0c;30歲對于程序員而言完全是一個38線&#xff0c;接著就是轉業轉崗的事情&#xff0c;這一點在業界也算是一個共識了。 大學畢業步入IT行業普遍年齡也是在22歲左右&#xff0c;然而…

java并發synchronized 鎖的膨脹過程(鎖的升級過程)深入剖析(2)

接下來我們分析兩個批量偏向撤銷的相關案例&#xff08;禁止偏向鎖延遲的情況下&#xff1a;-XX:UseBiasedLocking -XX:BiasedLockingStartupDelay0&#xff09;&#xff1a; 案例一&#xff1a; 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…

System.Configuration命名空間下的關鍵類

1.ConfigurationManager和 WebConfigurationManager類&#xff1a; 使用 ConfigurationManager 類&#xff0c;可以訪問計算機和應用程序的配置信息。ConfigurationManager 是處理客戶端應用程序配置文件的首選方法&#xff1b;不推薦使用任何其他方法。對于 Web 應用程序&…

連續四年百度Android崗必問面試題!Android校招面試指南

前言 剛從阿里面試回來&#xff0c;想和大家分享一些我的面試經驗&#xff0c;以及面試題目。 這篇文章將會更加聚焦在面試前需要看哪些資料&#xff0c;一些面試技巧以及一些這次的面試考題。 面試經歷 7月確定想走后開始看各種面經&#xff0c;復習基礎知識&#xff0c;月…

Spring Boot教程(11) – 理解注解@ControllerAdvice

之前&#xff0c;我們介紹過ModelAttribute和ExceptionHandler,前者可以往請求的Model里加數據&#xff0c;后者可以接受請求處理方法拋出的異常。但是他們放在控制器(Controller)里的時候&#xff0c;作用范圍是有限的&#xff0c;只管當前控制器里的方法。如果你有幾百個控制…

透徹解析!字節跳動Android實習面試涼涼經,年薪超過80萬!

什么是Kotlin? Kotlin&#xff0c;如前面所說&#xff0c;它是JetBrains開發的基于JVM的語言。JetBrains因為創造了一個強大的Java開發IDE被大家所熟知。Android Studio&#xff0c;官方的Android IDE&#xff0c;就是基于Intellij&#xff0c;作為一個該平臺的插件。 Kotli…

synchronized 底層如何實現?什么是鎖升級、降級?

synchronized 底層如何實現&#xff1f;什么是鎖升級、降級&#xff1f; synchronized 代碼塊是由一對 monitorenter/monitorexit 指令實現的&#xff0c;Monitor 對象是同步的基本實現單元。 https://docs.oracle.com/javase/specs/jls/se10/html/jls-8.html#d5e13622 在Jav…

Spring主要用到兩種設計模式

Spring主要用到兩種設計模式 1、工廠模式 Spring容器就是實例化和管理全部Bean的工廠。 工廠模式可以將Java對象的調用者從被調用者的實現邏輯中分離出來。 調用者只關心被調用者必須滿足的某種規則&#xff0c;這里的規則我們可以看做是接口&#xff0c;而不必關心實例的具體實…

意外收獲字節跳動內部資料,已開源

前言 每年的3、4月份是各大企業為明年拓展業務大量吸納人才的關鍵時期&#xff0c;招聘需求集中、空缺崗位多&#xff0c;用人單位也習慣在初秋進行大規模招聘。 金九銀十&#xff0c;招聘旺季&#xff0c;也是一個求職旺季。 不打無準備的仗&#xff0c;在這種關鍵時期&…

OpenJDK研究

這里以32位Windows 7為例 安裝必須的軟件 JDK1.8CygwinMicrosoft Visual Studio 2010 (請下載英文版) 這里就不介紹怎么安裝這些軟件了&#xff0c;假設安裝后的目錄名分別是: (請根據你的實際情況調整這些目錄名&#xff09; D:\JavaSE1.8 D:\Cygwin D:\VS2010 增加環境變…