Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 亚洲高清电影,国产视频第一页,中文一区二区

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Atom完整漢化詳解


          tom(一款開源的代碼編輯器)是github專門為程序員推出的一個跨平臺文本編輯器。具有簡潔和直觀的圖形用戶界面,并有很多有趣的特點:支持CSS,HTML,JavaScript等網頁編程語言。它支持宏,自動完成分屏功能,集成了文件管理器。

          用過Atom的朋友都知道,它是英文版的,那怎么把它變成中文呢

          很簡單,打開Atom

          我們選擇【file】—>【setting】

          然后選擇【Install】

          然后輸入【simplified-chinese-menu】然后點擊一下【Packages】進行搜索

          然后在下面就可以看到我們搜索出來的內容,看到和我們搜索的標題一模一樣,我們就是需要安裝它

          最后點擊右下角的【Install】來安裝我們的漢化包,安裝完成后不用重啟自動轉換成中文了

          這里看到我們已經漢化成功了


          -- END --

          喜歡就關注我每天get點計算機知識~

          tom編輯器

          最近在折騰Atom編輯器,寫Python進行交互運行還是很方便的,代碼提示什么的比Jupyter也好一些,還可以配置一堆插件,定制自己的開發環境。對于代碼整齊性來說,aligner插件是不能錯過的。

          atom-aligner是干什么的呢,就是用來對齊的,比如我們寫這樣一段變量賦值的程序:

          雖然也沒問題,但是如果能按=對齊就更強迫癥一些。安裝插件后,選中要對齊的行,按快捷鍵:

          Mac: ctrl-cmd-/ Linux/Windows: ctrl-alt-/

          就會變為這樣

          需要注意是,只安裝一個atom-aligner插件是不夠的,得配合不同語言的另一些插件,比如我寫python代碼,得另外裝一個aligner-python插件才可以。

          如果是JSON格式的對象,也是可以按冒號:對齊的

          要注意的是,如果連同=,{}一起選中,按快捷鍵對齊是不起作用的,得寫成這樣:

          對齊后是這樣的

          所以:左邊是不會對齊的,我們在編輯器寫代碼的時候一般都會左對齊了,像這樣

          有個問題是,官方的說明里說是可以冒號對齊的,對齊成這樣

          插件設置中沒有找到相關設置,官方說明看了半天也沒找到怎么設置,最后無奈改了插件的源碼。

          進入插件設置

          找到插件包配置,修改默認為left

          重啟編輯器就可以了。

          在前面的話

          正如我開篇所說,我們要整理一些java并發編程的學習文檔,這一篇就是第一篇:原子操作。 主要說什么是原子操作,如何實現原子操作以及java中的原子操作類。

          開酒,滿上

          什么是原子操作

          什么是原子操作,所謂原子操作,就是一個操作是不能打斷的操作。嗯.......確切的說應該是不被其他線程或者任務影響的操作。

          沒錯,原子操作就是你在家里的一次上廁所的操作 >> 進廁所,上鎖,執行操作..... 身心愉悅,開鎖,離開.....

          在程序中的體現就是一個線程在執行某個任務占用某個資源在操作的時候,不會被其他的線程或者任務搶走資源,直到這個任務結束釋放資源,其他的線程或者任務才能使用這個資源。

          嗯......其實就是我們說的給資源上鎖,就是同步操作。。。。。

          看看案例

          我們都知道 i++ 是給i變量加1。那么i++是不是原子操作呢?

          看看程序:

          先交代一下程序: 靜態成員變量x就是要操作的資源。 靜態方法incr()就是對x進行加1操作。在main方法中定義CountDownLatch對象,用來確保所有線程結束之后再輸出x的結果。 循環創建100個線程,每個線程調用incr()方法1000次,理論上x最終的值應該是100000。

           package com.qidian.atom;
           import java.util.concurrent.CountDownLatch;
           /**
            * @author 戴著假發的程序員
            */
           public class Test {
               // 靜態成員變量
               private static int x = 0;
               public static void main(String[] args) throws InterruptedException {
                   // 定義CountDownLatch 確保所有線程結束之后再輸出x的值
                   CountDownLatch cd = new CountDownLatch(100);
                   // 循環創建100個線程
                   for(int i = 0;i<100;i++){
                       new Thread("線程A"){
                           public void run() {
                               // 每個線程執行1000次incr方法
                               for (int j = 0;j<1000;j++){
                                   incr();
                               }
                               cd.countDown();
                           }
                       }.start();
                   }
                   // 確保所有線程結束之后再繼續執行
                   cd.await();
                   // 輸出x的值
                   System.out.println(x);
               }
               // incr方法,執行x ++
               public static void incr(){
                   x ++;
               }
           }

          多執行幾次,我們會發現x的值有時并不是100000.

          這樣的結果就是因為x ++不是原子操作。也就是說你在上廁所的時候沒有鎖門,導致有可能上到一半就被別人打斷了,所有你上的這100000次廁所,總有那么幾次可能沒有成功,身心沒有得到放松。。。。。。。

          看圖理解一下被打斷的情況:

          首先要知道 x++ 實際的操作是有兩步的 就是:

          第一步: int z = x + 1;

          第二步: x = z;

          說明一下:線程A和線程B同時給X執行++操作。

          線程A先獲取x的值,并且執行 z = x +1, 這時線程A打個盹,線程B獲取x的值,并且執行 z = x + 1,然后將z的值寫入x變量。 這時x就從10被修改為11。 這時線程A醒了,直接將自己計算的z(還是11)的值寫入x變量。就會導致x的值從11修改為11。這時我們會發現,線程B的加1操作被覆蓋了。這就導致線程B的這次+1操作失敗了。

          這就是上面程序產生的非100000的結果的原因。其實解決辦法非常簡單,就是給incr方法上鎖。任何一個線程操作x的時候,其他線程都必須等待。哪怕當前正在操作的線程打盹,其他線程也不能操作x。

          嗯!沒錯,這就是提醒你,上廁所一定要記得鎖門......永遠都無法忘記曾經發生在大學宿舍的凌晨廁所驚悚事件.....

          于是乎,程序可以是這樣:使用synchronized修飾incr方法,當然了加鎖的方法有很多種。我們后面的文章再說鎖的問題。

           package com.qidian.atom;
           
           import java.util.concurrent.CountDownLatch;
           ?
           /**
            * @author 戴著假發的程序員
            * @company 江蘇極刻知學-起點編程
            */
           public class Test {
               // 靜態成員變量
               private static int x = 0;
               public static void main(String[] args) throws InterruptedException {
                   // 定義CountDownLatch 確保所有線程結束之后再輸出x的值
                   CountDownLatch cd = new CountDownLatch(100);
                   // 循環創建100個線程
                   for(int i = 0;i<100;i++){
                       new Thread("線程A"){
                           public void run() {
                               // 每個線程執行1000次incr方法
                               for (int j = 0;j<1000;j++){
                                   incr();
                               }
                               cd.countDown();
                           }
                       }.start();
                   }
                   // 確保所有線程結束之后再繼續執行
                   cd.await();
                   // 輸出x的值
                   System.out.println(x);
               }
               // incr方法,執行x ++
               // 使用synchronized修飾incr方法,使其成為同步方法,其中的操作就無法被其他線程影響了
               public synchronized static void incr(){
                   x ++;
               }
           }

          加鎖之后,incr方法中的操作 i++ 就可以理解為原子操作了。當然我們也要明白,任何程序只要上鎖都會有效率問題。

          java中的原子操作類

          java中給我們提供了幾個原子操作類,這幾個類提供了好用的API。這些個API對資源的操作都是原子操作,不會有線程安全問題。我們來see see。。。。

          沒錯就是java.util.concurrent.atomic包下的所有類。

          我們來看幾個經典的類的使用吧!

          AtomicInteger

          這個類是一個int類型的原子操作類。

          看看他的AIP:

          構造方法:

          • AtomicInteger() 創建一個新的AtomicInteger,初始值為 0 。
          • AtomicInteger(int initialValue) 用給定的初始值創建一個新的AtomicInteger。

          來看看一些經典的API

          • addAndGet(int delta) 將給定的值原子地添加到當前值。并且返回更新后的int值。
          • compareAndSet(int expect, int update) 如果當前值 ==為預期值,則將該值原子設置為給定的更新值。 如果更新成功就返回true,否則返回false。
          • decrementAndGet() 原子減1當前值。并且返回更新后的值。
          • get() 獲取當前值。
          • getAndAdd(int delta) 將給定的值原子地添加到當前值。 并且返回更新前的值。
          • getAndDecrement() 原子減1當前值。 并且返回更新前的值。
          • getAndIncrement() 原子上增加一個當前值。 并且返回更新前的值。
          • getAndSet(int newValue) 將原子設置為給定值并返回舊值。
          • incrementAndGet() 原子上增加1。并且返回更新后的值。
          • set(int newValue) 設置為給定值。

          來來來!先根據程序理解一下API。 后面我們來翻翻源碼,看看有沒有啥問題。。。。

           package com.qidian.atom;
           ?
           import java.util.concurrent.atomic.AtomicInteger;
           ?
           /**
            * @author 戴著假發的程序員
            */
           public class Test1 {
               public static void main(String[] args) {
                   AtomicInteger atomi = new AtomicInteger(10);
                   // 將給定的值原子地添加到當前值。
                   int i = atomi.addAndGet(10);
                   System.out.println(i);// 20
                   //  如果當前值 ==為預期值,則將該值原子設置為給定的更新值。 如果更新成功就返回true,否則返回false。
                   boolean b = atomi.compareAndSet(20, 30);
                   System.out.println(b); // true
                   System.out.println(atomi.get());// 30
                   // 原子減1當前值。并且返回更新后的值。
                   i = atomi.decrementAndGet();
                   System.out.println(i); // 29
                   System.out.println(atomi.get()); // 29
                   // 將給定的值原子地添加到當前值。 并且返回更新前的值。
                   i = atomi.getAndAdd(50);
                   System.out.println(i); // 29
                   System.out.println(atomi.get()); // 79
                   // 原子減1當前值。  并且返回更新前的值。
                   i = atomi.getAndDecrement();
                   System.out.println(i);// 79
                   System.out.println(atomi.get());// 78
                   // 原子上增加一個當前值。 并且返回更新前的值。
                   i = atomi.getAndIncrement();
                   System.out.println(i);// 78
                   System.out.println(atomi.get());// 79
                   //  將原子設置為給定值并返回舊值。
                   i = atomi.getAndSet(100);
                   System.out.println(i);// 79
                   System.out.println(atomi.get());// 100
                   // 原子上增加一個當前值。并且返回更新后的值。
                   i = atomi.incrementAndGet();
                   System.out.println(i);// 101
                   System.out.println(atomi.get());// 101
               }
           }

          OK! 看看源碼:

          嗯。。。。。。。。。先看看addAndGet的源碼: AtomicInteger中的源碼:

           /**
            * Atomically adds the given value to the current value.
            *
            * @param delta the value to add
            * @return the updated value
            */
           public final int addAndGet(int delta) {
               return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
           }

          原來使用的是unsafe類中的方法,ok我們繼續翻:

           public final int getAndAddInt(Object var1, long var2, int var4) {
               int var5;
               do {
                   //  volatile 嗯..... 歡迎持續關注,后面的文章中會有這個關鍵字的詳解
                   var5 = this.getIntVolatile(var1, var2);
                   // 這里最終是使用 unsafe的compareAndSwapInt替換數據。
               } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
           ?
               return var5;
           }

          嗯嗯嗯嗯! 再來看看別的方法:

          compareAndSet方法的源碼:

           public final boolean compareAndSet(int expect, int update) {
               return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
           }

          decrementAndGet方法源碼:

           public final int decrementAndGet() {
               return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
           }

          getAndAdd方法源碼:

           public final int getAndAdd(int delta) {
               return unsafe.getAndAddInt(this, valueOffset, delta);
           }

          看到這里我們想差不多夠了,我們也明白了, AtomicInteger類中的所有的API,做到原子操作更新值的底層都是通過unsafe的CAS實現的。

          那么unsafe的CAS有沒有問題呢? (關于CAS后面的文章會有詳細說明,歡迎關注,你懂的,我最喜歡你關注我了。( ^__^ ) 嘻嘻…… )

          當然都看到這里了,就點個攢吧!

          AtomicReference

          看看這個類的說明:

           public class AtomicReference<V>
           extends Object
           implements Serializable
           可以原子更新的對象引用。

          說白了就是對任何引用類型的原子操作類。

          直接上菜:

           /**
            * @author 戴著假發的程序員
            * @company 江蘇極刻知學-起點編程
            */
           public class Test3 {
               public static void main(String[] args) {
                   AtomicReference<String> ar = new AtomicReference<>("卡卡西");
                   System.out.println(ar.get()); // 卡卡西
                   String s = ar.get();
                   boolean b = ar.compareAndSet(s, "旗木卡卡西");
                   System.out.println(b);// true
                   System.out.println(ar.get());// 旗木卡卡西
               }
           }

          嗯。。。。。應該不難理解吧!

          CAS有個小問題那就是ABA問題。 休息5分鐘......... 可以瀏覽一下我的其他文章......

          ABA問題

          來吧,我來稍微聊聊ABA問題。

          先看看CAS: CAS的全拼是:Compare And Swap 就是比較并且替換。什么意思呢?看圖吧

          稍微說明一下:一個容器里面有一只雞,小明同學左手一只雞,右手一只鴨。左手的雞就是容器里面的預期值,如果左手的小雞和容器里面的小雞一樣,就拿右手的小鴨把容器里的小雞替換了。如果左手的預期值和容器里的小雞不一樣,那就算了,就不換了。

          OK,看看ABA問題:

          這個圖好亂,看看我們的描述吧:

          ①小明小明準備把一只雞替換成一只鴨,于是就查看了容器里的內容,發現是一只雞,他就在右手抓起一只雞作為預期值。準備執行CAS。 可是就在這時,小明肚子疼,去廁所了。 如花就開始了....

          ②③藍色的如花使用CAS將容器里面的一只雞替換成一頭豬。

          ④⑤如花不知道為什么感覺雞不劃算,于是乎又使用另外一只雞把豬給換回來了。

          ⑥這時小明上好廁所了,于是乎拿起自己之前準備好的雞開始和容器里面的小雞進行比較,發現果然一樣,于是就使用一只鴨替換了容器里面的雞。

          在這個過程中,問題就是小明一開始按照容器里面的內容,準備了一只雞,再他上完廁所再回來比較的時候,容器里面的雞已經被如花替換過了,已經不是以前的雞了。所謂此雞非彼雞。所以理論上小明的CAS應該不能成功的,但是結果缺失成功的,這就是經典的ABA問題。

          看程序理解一下

           package com.qidian.atom;
           ?
           import java.util.concurrent.TimeUnit;
           import java.util.concurrent.atomic.AtomicInteger;
           ?
           /**
            * @author 戴著假發的程序員
            */
           public class Test2 {
               // 原始值為 3
               private static AtomicInteger ai = new AtomicInteger(3);
           ?
               public static void main(String[] args) {
                    // 啟動線程1
                   new Thread(){
                       public void run() {
                           // 取出原來的值
                           int i = ai.get();// 這個值作為預期值
                           //打個盹
                           try {
                               TimeUnit.MICROSECONDS.sleep(50);
                           } catch (InterruptedException e) {
                               e.printStackTrace();
                           }
                           // 使用cas替換
                           boolean b = ai.compareAndSet(i, 4);
                           System.out.println("線程1將3替換為4:"+b);
                       }
                   }.start();
                   // 再來一個線程
                   new Thread(){
                       public void run() {
                           // 取出原來的值
                           int i = ai.get();
                           // 將3替換為4
                           boolean b = ai.compareAndSet(i, 4);
                           System.out.println("線程2將3替換為4:"+b);
                           // 再次將4替換為3
                           b = ai.compareAndSet(4, 3);
                           System.out.println("線程2將4替換為3:"+b);
                       }
                   }.start();
               }
           }

          結果:

          理論上線程1之前取出的3已經不是現在的3了,但是依然替換成功了。

          原子操作類也有ABA問題,于是乎準備了一個新的解決方案,那就是AtomicStampedReference類。

          AtomicStampedReference

          看看這個類的說明:

           public class AtomicStampedReference<V>
           extends Object
               一個AtomicStampedReference維護對象引用以及整數“印記”,可以原子更新。

          關鍵字:“印記”。 什么意思呢?

          這個類為了解決ABA問題,給每個值增了一個“印記”,就好像,小明會給自己的雞蓋上一個印章,如花也會給自己的雞蓋上一個印章,這樣的話我們就可以通過印章輕松判斷,此雞是否是彼雞。

          看看構造方法:

          AtomicStampedReference(V initialRef, int initialStamp) 創建一個新的 AtomicStampedReference與給定的初始值。

          這個類的API有限,但是有兩個比較經典的方法:

          • boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) 以原子方式設置該引用和郵票給定的更新值的值,如果當前的參考是 ==至預期的參考,并且當前標志等于預期標志。
          • V getReference() 返回引用的當前值。
          • int getStamp() 返回郵票的當前值。
          • void set(V newReference, int newStamp) 無條件地設置引用和戳記的值。

          看程序理解理解

           package com.qidian.atom;
           ?
           import java.util.concurrent.TimeUnit;
           import java.util.concurrent.atomic.AtomicStampedReference;
           ?
           /**
            * @author 戴著假發的程序員
            */
           public class Test4 {
               private static AtomicStampedReference<String> asr = new AtomicStampedReference<>("卡卡西",1);
               public static void main(String[] args) {
                   // 線程1
                   new Thread(){
                       public void run() {
                           // 取值
                           String s = asr.getReference();
                           // 獲取印記
                           int stamp = asr.getStamp();
                           // 打盹
                           try {
                               TimeUnit.MICROSECONDS.sleep(500);
                           } catch (InterruptedException e) {
                               e.printStackTrace();
                           }
                           // 替換值  參數說明(原始值,新值,原始印記,新的印記)
                           boolean b = asr.compareAndSet("卡卡西","旗木卡卡西",stamp,stamp+1);
                           System.out.println("線程1,替換的結果:"+b);
                       }
                   }.start();
                   // 線程1
                   new Thread(){
                       public void run() {
                           // 取值
                           String s = asr.getReference();
                           // 獲取印記
                           int stamp = asr.getStamp();
                           // 第一次替換值  參數說明(原始值,新值,原始印記,新的印記)
                           boolean b = asr.compareAndSet("卡卡西","旗木五五開",stamp,stamp+1);
                           System.out.println("線程2,第一次替換的結果:"+b);
                           // 第二次替換值  參數說明(原始值,新值,原始印記,新的印記)
                           b = asr.compareAndSet("旗木五五開","卡卡西",stamp,stamp+1);
                           System.out.println("線程2,第二次替換的結果:"+b);
                       }
                   }.start();
               }
           }

          結果:

          稍微說明一下:

          由于每次在CAS的比較中,不光要比較預期值,還要比較”印記“。而每次替換的時候都不光要提換值,還要修改印記,所以即使值一樣,如果印記不一樣,依然會替換失敗。

          所以上面的程序就是線程2量次替換,第二次把”旗木五五開“替換回”卡卡西“之后,這個"卡卡西"的印記已經不是之前的”卡卡西“的印記了,所以線程1打盹結束之后,再去CAS就失敗啦!

          其他的原子類

          至于其他的原子類嗎!!!! 嗯?。?!都差不多,就是多幾個類型唄,比如AtomicLong,AtomicIntegerArray之類的。就不在贅述了。

          還有一些不咋用的,我就不詳細說了,有興趣可以研究研究,我們討論討論。

          這一篇就到這里了。 歡迎關注,記得點贊。。。。。。。。


          作者:戴著假發的程序員
          鏈接:https://juejin.cn/post/7071530851684122660


          主站蜘蛛池模板: 精品日韩一区二区三区视频| 国产剧情国产精品一区| 无码人妻AⅤ一区二区三区水密桃| 亚洲国产成人久久一区WWW | 成人无码AV一区二区| 人妻无码第一区二区三区| 精品在线一区二区三区| 亚洲综合无码一区二区| 精品国产一区二区三区AV性色| 国产怡春院无码一区二区| 3d动漫精品啪啪一区二区中| 国产亚洲一区二区手机在线观看 | 亚洲AV无码一区二区三区电影| 丰满爆乳一区二区三区| 在线视频一区二区三区三区不卡| 视频一区视频二区制服丝袜 | 亚洲色精品vr一区二区三区| 午夜视频久久久久一区 | 精品国产香蕉伊思人在线在线亚洲一区二区 | 国产AV国片精品一区二区| 国产一区二区三区日韩精品| 人妻在线无码一区二区三区| 中文字幕日韩丝袜一区| 亚洲欧美日韩一区二区三区在线| 国产精品一区在线播放| 激情内射亚洲一区二区三区爱妻 | 韩日午夜在线资源一区二区 | 久久久久人妻一区二区三区vr| 亚洲熟女少妇一区二区| 久久精品国产一区二区三| 国产一区视频在线| 奇米精品一区二区三区在线观看| 久久青草国产精品一区| 亚洲午夜精品一区二区| 3D动漫精品啪啪一区二区下载| 欧美日韩精品一区二区在线视频| 国产精品乱码一区二区三 | 中文字幕一区二区三区5566| 韩国一区二区视频| 日韩成人一区ftp在线播放| 一区二区三区中文|