1. 程式人生 > 實用技巧 >第八次 Hive 操作與應用 詞頻統計

第八次 Hive 操作與應用 詞頻統計

2.4 原子性

概述 : 所謂的原子性是指在一次操作或者多次操作中,要麼所有的操作全部都得到了執行並且不會受到任何因素的干擾而中斷,要麼所有的操作都不執行,多個操作是一個不可以分割的整體。

程式碼實現 :

package com.itheima.threadatom;
​
public class AtomDemo {
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();
​
        for (int i = 0; i < 100; i++) {
            
new Thread(atom).start(); } } } class MyAtomThread implements Runnable { private volatile int count = 0; //送冰淇淋的數量 ​ @Override public void run() { for (int i = 0; i < 100; i++) { //1,從共享資料中讀取資料到本執行緒棧中. //2,修改本執行緒棧中變數副本的值 //3,會把本執行緒棧中變數副本的值賦值給共享資料.
count++; System.out.println("已經送了" + count + "個冰淇淋"); } } }

程式碼總結 : count++ 不是一個原子性操作, 他在執行的過程中,有可能被其他執行緒打斷

2.5 volatile關鍵字不能保證原子性

解決方案 : 我們可以給count++操作添加鎖,那麼count++操作就是臨界區中的程式碼,臨界區中的程式碼一次只能被一個執行緒去執行,所以count++就變成了原子操作。

package com.itheima.threadatom2;
​
public
class AtomDemo { public static void main(String[] args) { MyAtomThread atom = new MyAtomThread(); ​ for (int i = 0; i < 100; i++) { new Thread(atom).start(); } } } class MyAtomThread implements Runnable { private volatile int count = 0; //送冰淇淋的數量 private Object lock = new Object(); ​ @Override public void run() { for (int i = 0; i < 100; i++) { //1,從共享資料中讀取資料到本執行緒棧中. //2,修改本執行緒棧中變數副本的值 //3,會把本執行緒棧中變數副本的值賦值給共享資料. synchronized (lock) { count++; System.out.println("已經送了" + count + "個冰淇淋"); } } } }

2.6 原子性_AtomicInteger

概述:java從JDK1.5開始提供了java.util.concurrent.atomic包(簡稱Atomic包),這個包中的原子操作類提供了一種用法簡單,效能高效,執行緒安全地更新一個變數的方式。因為變

量的型別有很多種,所以在Atomic包裡一共提供了13個類,屬於4種類型的原子更新方式,分別是原子更新基本型別、原子更新陣列、原子更新引用和原子更新屬性(欄位)。本次我們只講解

使用原子的方式更新基本型別,使用原子的方式更新基本型別Atomic包提供了以下3個類:

AtomicBoolean: 原子更新布林型別

AtomicInteger: 原子更新整型

AtomicLong: 原子更新長整型

以上3個類提供的方法幾乎一模一樣,所以本節僅以AtomicInteger為例進行講解,AtomicInteger的常用方法如下:

public AtomicInteger():                 初始化一個預設值為0的原子型Integer
public AtomicInteger(int initialValue):  初始化一個指定值的原子型Integer
​
int get():                              獲取值
int getAndIncrement():                   以原子方式將當前值加1,注意,這裡返回的是自增前的值。
int incrementAndGet():                   以原子方式將當前值加1,注意,這裡返回的是自增後的值。
int addAndGet(int data):                 以原子方式將輸入的數值與例項中的值(AtomicInteger裡的value)相加,並返回結果。
int getAndSet(int value):                以原子方式設定為newValue的值,並返回舊值。

程式碼實現 :

package com.itheima.threadatom3;
​
import java.util.concurrent.atomic.AtomicInteger;
​
public class MyAtomIntergerDemo1 {
//    public AtomicInteger():                  初始化一個預設值為0的原子型Integer
//    public AtomicInteger(int initialValue): 初始化一個指定值的原子型Integer
    public static void main(String[] args) {
        AtomicInteger ac = new AtomicInteger();
        System.out.println(ac);
​
        AtomicInteger ac2 = new AtomicInteger(10);
        System.out.println(ac2);
    }
​
}
package com.itheima.threadatom3;
​
import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicInteger;
​
public class MyAtomIntergerDemo2 {
//    int get():                獲取值
//    int getAndIncrement():     以原子方式將當前值加1,注意,這裡返回的是自增前的值。
//    int incrementAndGet():     以原子方式將當前值加1,注意,這裡返回的是自增後的值。
//    int addAndGet(int data):   以原子方式將引數與物件中的值相加,並返回結果。
//    int getAndSet(int value):  以原子方式設定為newValue的值,並返回舊值。
    public static void main(String[] args) {
//        AtomicInteger ac1 = new AtomicInteger(10);
//        System.out.println(ac1.get());
//        AtomicInteger ac2 = new AtomicInteger(10);
//        int andIncrement = ac2.getAndIncrement();
//        System.out.println(andIncrement);
//        System.out.println(ac2.get());
//        AtomicInteger ac3 = new AtomicInteger(10);
//        int i = ac3.incrementAndGet();
//        System.out.println(i);//自增後的值
//        System.out.println(ac3.get());
//        AtomicInteger ac4 = new AtomicInteger(10);
//        int i = ac4.addAndGet(20);
//        System.out.println(i);
//        System.out.println(ac4.get());
​
        AtomicInteger ac5 = new AtomicInteger(100);
        int andSet = ac5.getAndSet(20);
        System.out.println(andSet);
        System.out.println(ac5.get());
    }
}

2.7 AtomicInteger-記憶體解析

AtomicInteger原理 : 自旋鎖 + CAS 演算法

CAS演算法:

有3個運算元(記憶體值V, 舊的預期值A,要修改的值B)

當舊的預期值A == 記憶體值 此時修改成功,將V改為B

當舊的預期值A!=記憶體值 此時修改失敗,不做任何操作

並重新獲取現在的最新值(這個重新獲取的動作就是自旋)

2.8 AtomicInteger-原始碼解析

程式碼實現 :

package com.itheima.threadatom4;
​
public class AtomDemo {
    public static void main(String[] args) {
        MyAtomThread atom = new MyAtomThread();
​
        for (int i = 0; i < 100; i++) {
            new Thread(atom).start();
        }
    }
}
package com.itheima.threadatom4;
​
import java.util.concurrent.atomic.AtomicInteger;
​
public class MyAtomThread implements Runnable {
    //private volatile int count = 0; //送冰淇淋的數量
    //private Object lock = new Object();
    AtomicInteger ac = new AtomicInteger(0);
​
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //1,從共享資料中讀取資料到本執行緒棧中.
            //2,修改本執行緒棧中變數副本的值
            //3,會把本執行緒棧中變數副本的值賦值給共享資料.
            //synchronized (lock) {
//                count++;
//                ac++;
            int count = ac.incrementAndGet();
            System.out.println("已經送了" + count + "個冰淇淋");
           // }
        }
    }
}

原始碼解析 :


//先自增,然後獲取自增後的結果
public final int incrementAndGet() {
        //+ 1 自增後的結果
        //this 就表示當前的atomicInteger(值)
        //1    自增一次
        return U.getAndAddInt(this, VALUE, 1) + 1;
}
​
public final int getAndAddInt(Object o, long offset, int delta) {
        //v 舊值
        int v;
        //自旋的過程
        do {
            //不斷的獲取舊值
            v = getIntVolatile(o, offset);
            //如果這個方法的返回值為false,那麼繼續自旋
            //如果這個方法的返回值為true,那麼自旋結束
            //o 表示的就是記憶體值
            //v 舊值
            //v + delta 修改後的值
        } while (!weakCompareAndSetInt(o, offset, v, v + delta));
            //作用:比較記憶體中的值,舊值是否相等,如果相等就把修改後的值寫到記憶體中,返回true。表示修改成功。
            //                                 如果不相等,無法把修改後的值寫到記憶體中,返回false。表示修改失敗。
            //如果修改失敗,那麼繼續自旋。
        return v;
}

2.9 悲觀鎖和樂觀鎖

synchronized和CAS的區別 :

相同點:在多執行緒情況下,都可以保證共享資料的安全性。

不同點:synchronized總是從最壞的角度出發,認為每次獲取資料的時候,別人都有可能修改。所以在每 次操作共享資料之前,都會上鎖。(悲觀鎖)

cas是從樂觀的角度出發,假設每次獲取資料別人都不會修改,所以不會上鎖。只不過在修改共享資料的時候,會檢查一下,別人有沒有修改過這個資料。

如果別人修改過,那麼我再次獲取現在最新的值。

如果別人沒有修改過,那麼我現在直接修改共享資料的值.(樂觀鎖)