併發編程基礎知識二

volatile關鍵字的做用是變量在多個線程之間可見
volatile的做用是強制線程到主內存(共享內存)裏讀取變量,而不是線程工做內存區裏去讀取變量,從而實現了多個線程之間的變量可見,也就是知足線程安全的可見性。java

private volatile boolean isRunning = true;
    private void setRunning(boolean isRunning){
        this.isRunning = isRunning;
    }
    
    public void run(){
        System.out.println("進入run方法..");
        int i = 0;
        while(isRunning == true){
            //..
        }
        System.out.println("線程中止");
    }

volatile 關鍵字雖然擁有多個線程之間的可見性,可是卻不具有原子性
volatile關鍵字用於針對多個線程可變的變量操做,可是不能替代synchronized關鍵字的同步功能。小程序


atomic類支持原子性操做安全

private static AtomicInteger count = new AtomicInteger(0);
    
    
    /**synchronized*/
    public synchronized int multiAdd(){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count.addAndGet(1);
            count.addAndGet(2);
            count.addAndGet(3);
            count.addAndGet(4); //+10
            return count.get();
    }

多個addAndGet在一個方法內是非原子性的,須要加synchronized進行修飾,保證4個addAndGet總體原子性多線程


線程通訊
使用wait和notify能夠實現線程之間的通訊併發

  1. wait/notify必須配合synchronized 關鍵字使用
  2. wait釋放鎖 notify不釋放鎖
  3. 全部的object均可以使用該方法
// final Object lock = new Object();
    final CountDownLatch countDownLatch = new CountDownLatch(1);
    
    Thread t1 = new Thread(new Runnable() {
    
            public void run() {

                //synchronized (lock) {
                    try {
                        
                        //countDownLatch.countDown();
                        //countDownLatch.awat();
                        //lock.notify();
                        //lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                //}
            }
    }, "t1");

使用wait/nofity的缺點是沒法實現實時的通訊 推薦使用countDownLatch 來實現實時的交互ide


使用wait/notify模擬Queue性能

public class MyQueue {
    
    //1 須要一個承裝元素的集合 
    private LinkedList<Object> list = new LinkedList<Object>();
    
    //2 須要一個計數器
    private AtomicInteger count = new AtomicInteger(0);
    
    //3 須要制定上限和下限
    private final int minSize = 0;
    
    private final int maxSize ;
    
    //4 構造方法
    public MyQueue(int size){
        this.maxSize = size;
    }
    
    //5 初始化一個對象 用於加鎖
    private final Object lock = new Object();
    
    
    //put(anObject): 把anObject加到BlockingQueue裏,
//若是BlockQueue沒有空間,則調用此方法的線程被阻斷,
//直到BlockingQueue裏面有空間再繼續.
    public void put(Object obj){
        synchronized (lock) {
            while(count.get() == this.maxSize){
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //1 加入元素
            list.add(obj);
            //2 計數器累加
            count.incrementAndGet();
            //3 通知另一個線程(喚醒)
            lock.notify();
            System.out.println("新加入的元素爲:" + obj);
        }
    }
    
    
    //take: 取走BlockingQueue裏排在首位的對象,
//若BlockingQueue爲空,
//阻斷進入等待狀態直到BlockingQueue有新的數據被加入.
    public Object take(){
        Object ret = null;
        synchronized (lock) {
            while(count.get() == this.minSize){
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //1 作移除元素操做
            ret = list.removeFirst();
            //2 計數器遞減
            count.decrementAndGet();
            //3 喚醒另一個線程
            lock.notify();
        }
        return ret;
    }
    
    public int getSize(){
        return this.count.get();
    }
    
    
    public static void main(String[] args) {
        
        final MyQueue mq = new MyQueue(5);
        mq.put("a");
        mq.put("b");
        mq.put("c");
        mq.put("d");
        mq.put("e");
        
        System.out.println("當前容器的長度:" + mq.getSize());
        
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                mq.put("f");
                mq.put("g");
            }
        },"t1");
        
        t1.start();
        
        
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Object o1 = mq.take();
                System.out.println("移除的元素爲:" + o1);
                Object o2 = mq.take();
                System.out.println("移除的元素爲:" + o2);
            }
        },"t2");
        
        
        try {
            //代替Thread.sleep(1000);
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        t2.start();
        
        
    }
    
    
    
}

TimeUnit是java.util.concurrent包下面的一個類,TimeUnit提供了可讀性更好的線程暫停操做,一般用來替換Thread.sleep()this

每次寫一個具備設計意義的小程序 考驗的是分析能力atom


ThreadLocal:
ThreadLocal 是線程局部變量 是一種多線程間 併發訪問變量 的解決方案
ThreadLocal 徹底不提供鎖,以空間換時間的方式 爲每個線程提供變量的獨立副本 以保障線程安全線程

private static ThreadLocal<String> th = new ThreadLocal();

在併發量不高 的時候 ,加鎖的性能會更好
座位一套無鎖的線程安全解決方案 使用ThreadLocal能夠減小所競爭


單例模式+多線程
在提升性能的時候 有保證了線程安全

  1. dubble check Instance
public class DubbleSingleton {

    private static DubbleSingleton ds;
    
    public  static DubbleSingleton getDs(){
        if(ds == null){
            try {
                //模擬初始化對象的準備時間...
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (DubbleSingleton.class) {
                if(ds == null){
                    ds = new DubbleSingleton();
                }
            }
        }
        return ds;
    }
}
  1. static inner class
public class Singleton {
    
    private static class InnerSingleton {
        private static Singleton single = new Singleton();
    }
    
    public static Singleton getInstance(){
        return InnerSingleton.single;
    }
    
}
相關文章
相關標籤/搜索