線程安全之不可變對象

以下內容裝載於
慕課網《Java併發編程與高併發解決方案》
《深入理解java虛擬機》第二版,386頁~387頁

1.什麼是不可變對象?

不可變對象一定是線程安全的,因爲無論是方法的實現者還是調用者,都不需要採用任何的線程安全保護措施;不可變對象一經創建以後他的狀態就不能被改變,對象的所有域都是final類型的;對象是正確創建的(在創建對象的期間沒有發生,this引用逸出)。

2.聲明不可變對象的方式有哪些?

2.1使用final修飾

使用final關鍵字修飾,我們應該都不陌生。

  1. 修飾類:該類不能被繼承,該類所有的方法都不能被重寫(常見的final類有String,基本數據類型的包裝類,BigInteger,BigDecimal)
  2. 修飾方法:方法不能被繼承類重寫
  3. 修飾變量:一個final變量實質上時是一個常量(當一個變量修飾基本數據類型的時候,變量的值不可以改變;當用final修飾引用類型的時候,此時變量不可以指向別的地址,但是注意可以修改該引用裏面的值。也正因爲這樣,當該引用變量爲全局變量的時候,就可能會引發線程安全問題)

注:final修飾變量的時候賦值時機:
final修飾成員變量

  1. 在聲明變量的等號右邊直接賦值;
  2. 在構造函數中賦值;
  3. 在類的初始代碼塊中賦值。

final修飾全局靜態變量

  1. 在聲明變量的等號右邊直接賦值;
  2. 在static代碼中賦值,但是不能在普通代碼塊彙總賦值。

final修飾局部變量

  1. 在聲明變量的等號右邊直接賦值;
  2. final修飾局部變量的時候,不指定賦值時機,只是要求在使用前必須賦值(和普通的成員變量用法一致);

示例代碼:final修飾基礎數據類型和引用類型的變量的區別。

package com.mark.example.immutable;

import com.google.common.collect.Maps;
import com.mark.annotations.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

@Slf4j
@NotThreadSafe
public class ImmutableTest1 {

    private final static Integer a = 1;
    private final static String b = "2";
    private final static Map<Integer, Integer> map = Maps.newHashMap();

    static {
        map.put(1, 2);
        map.put(3, 4);
        map.put(5, 6);
    }

    public static void main(String[] args) {
//        a = 2; final修飾的基礎數據類型不可以改變
//        b = "3";
//        map = Maps.newHashMap(); //final修飾的引用類型變量,引用類型不可以指向新的地址
        map.put(1, 3);//但是注意可以修改引用類型裏的值【引發線程不安全】
        log.info("{}", map.get(1));
    }

    private void test(final int a) {//final也可以用來修飾局部變量
//        a = 1; //那麼在局部變量的值也是不可以修改的
    }
}

總結:修飾基礎數據類型後,變量的值不可以修改;當final修飾引用類型的時候,變量執行執行的地址不可以修改,但是可以修改變量的屬性值。

2.2使用 Collections.unmodifiableXXX

示例代碼:使用JDK自帶的Collections類聲明不可變的對象

**package com.mark.example.immutable;

import com.google.common.collect.Maps;
import com.mark.annotations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.Map;

/**
 * author:Mark
 * date:2018/7/31  8:09
 * 線程安全不可變對象: Collections.unmodifiableXXXX
 */
@Slf4j
@ThreadSafe
public class ImmutableTest2 {
    private static Map<Integer, Integer> map = Maps.newHashMap();

    static {
        map.put(1, 2);
        map.put(3, 4);
        map.put(5, 6);
        map = Collections.unmodifiableMap(map);
    }

    public static void main(String[] args) {
        map.put(1, 3); //拋出異常,不可以改變final引用變量中的數據
        log.info("{}", map.get(1));
    }
}

2.3使用ImmutableXXX

實例代碼:使用第三方(guava)包,聲明不可變對象

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>23.0</version>
</dependency>
package com.mark.example.immutable;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.mark.annotations.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.Collections;
import java.util.Map;

/**
 * author:Mark
 * date:2018/7/31  8:09
 * ImmutableXXX(com.google.common.collect)第三方包Guava
 */
@Slf4j
@ThreadSafe
public class ImmutableTest3 {

    private final static ImmutableList<Integer> list = ImmutableList.of(1, 2, 3);

    private final static ImmutableSet set = ImmutableSet.copyOf(list);

    private final static ImmutableMap<Integer, Integer> map = ImmutableMap.of(1, 2, 3, 4);//key-value,key-value

    private final static ImmutableMap<Integer, Integer> map2 = ImmutableMap.<Integer, Integer>builder()
            .put(1, 2).put(3, 4).put(5, 6).build();


    public static void main(String[] args) {
        //list.add(4);//拋出異常
       // set.add(4);//拋出異常
        System.out.println(map2.get(3));
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章