1. 程式人生 > >Java併發程式設計---volatile關鍵字與atomic原子類

Java併發程式設計---volatile關鍵字與atomic原子類

一.Volatile關鍵字

     1.1 概念

           volatile關鍵字的主要作用是使變數在多個執行緒間可見

   1.2 示例程式

package com.thread.variable;

/**
 * volatile關鍵字的使用
 * @author lhy
 * @time   2017.12.23
 *
 */
public class RunThread extends Thread {

	//如果此處不使用volatile關鍵字,那麼程式執行結束,執行緒也不會終止,因為執行緒的執行取值是在記憶體區,和程式變數的定義無關
	private volatile boolean isRunning = true;
	
	private void setRunning(boolean isRunning){
		this.isRunning = isRunning;
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("進入run方法...");
		while(isRunning == true){
			boolean a = isRunning;
		}
		System.out.println("執行緒終止...");
	}

	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
        RunThread rt = new RunThread();
        rt.start();
        Thread.sleep(3000);
        rt.setRunning(false);
        System.out.println("isRunning的值已經被設定了false");
        Thread.sleep(1000);
        System.out.println(rt.isRunning);
	}

}

    1.3 程式碼分析

      在java中,每一個執行緒都會有一塊工作記憶體區,其中存放著所有執行緒共享的主記憶體中的變數值的拷貝.當執行緒執行時,它在自己的工作記憶體區中操作這些變數.為了存取一個共享的變數,一個執行緒通常先獲取鎖定並去清除它的記憶體工作區,把這些共享變數從所有執行緒的共享記憶體區中正確地裝入到他自己所在工作記憶體區中,當執行緒解鎖時保證該工作記憶體區中變數的值寫回到共享記憶體中.

      一個執行緒可以執行的操作有使用(use),賦值(assign),裝載(load),儲存(store),鎖定(lock),解鎖(unlock)

      而主記憶體可以執行的操作有讀(Read),寫(write),鎖定(lock),解鎖(unlock),每個操作都是原子的

      volatile的作用就是強制執行緒到主記憶體(共享記憶體)裡去讀取變數,而不去執行緒工作記憶體區裡去讀取,從而實現了多個執行緒間的變數可見,也就是滿足執行緒安全的可見性

二.Volatile關鍵字的非原子性

    2.1 概念

           volatile關鍵字雖然擁有多個執行緒之間的可見性,但是卻不具備同步性(也就是原子性),可以算上是一個輕量級的synchronized強很多,不會造成阻塞(在很多開源的架構裡,比如netty的底層程式碼就大量使用volatile,可見netty效能一定是非常不錯的).這裡需要注意.一般volatile用於只針對於多個執行緒可見的變數操作,並不能代替synchronized的同步功能.

     2.2 程式碼示例

package com.thread.variable;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * volatile關鍵字的非原子性
 * @author lhy
 * @time   2017.12.23
 *
 */
public class VolatileNoAtomic extends Thread {

//	private static volatile int count;	
	private static AtomicInteger count = new AtomicInteger(0);
	private static void addCount(){
		for(int i=0; i<1000; i++){
//			count++;
			count.incrementAndGet();
		}
		System.out.println(count);
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		addCount();
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
        VolatileNoAtomic[] arr = new VolatileNoAtomic [10];
        for(int i = 0 ; i<10; i++){
        	arr[i] = new VolatileNoAtomic();
        }
        
        for(int i=0 ; i<10 ; i++){
        	arr[i].start();
        }
	}

}

    2.3 程式碼分析

volatile關鍵字只具有可見性,沒有原子性.

三. atomic原子類

     3.1.概念

       要實現原子性,建議使用atomic類的系列物件,支援原子性操作.(注意atomic類只保證本身方法原子性,並不保證多次操作的原子性.除非在方法或物件上加synchronized鎖)

    3.2 程式碼示例

package com.thread.variable;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicUse {
    /**
     * 只能保證首個方法的原子性,不能保證整體的原子性,加鎖synchronized後可以保證整體的原子性
     * AtomicInteger原子性操作
     */
	private static AtomicInteger count = new AtomicInteger(0);
	
	//多個addAndGet在一個方法內是原子性的,需要加synchronized進行修飾,保證4個addAndGet整體原子性
	
	public int multiAdd(){
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		count.addAndGet(1);
		count.addAndGet(2);
		count.addAndGet(3);
		count.addAndGet(4);
		return count.get();
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        final AtomicUse au = new AtomicUse();
        
        List<Thread> ts = new ArrayList<Thread>();
        for(int i=0; i<100; i++){
        	ts.add(new Thread(new Runnable() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					System.out.println(au.multiAdd());
				}
			}));
        }
        
        for(Thread t : ts){
        	t.start();
        }
	}

}