JAVA併發之多線程基礎(5)

上面介紹了併發編程中的柵欄等JAVA併發之多線程基礎(4) 。經過惟一的一個終點線來幫助肯定線程是多晚開始執行下一次操做。java

LockSupport

提供了一個比較底層的線程掛起操做。有點相似於suspend()方法,可是這個方法不建議使用。編程

  1. park()使得當前線程掛起。裏面也是調用了Unsafe類進行底層操做。
public static void park() {
        UNSAFE.park(false, 0L);
    }
複製代碼

2.unpark(Thread thread)是將當前線程繼續往下執行。數組

public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }
複製代碼

LockSupport上面的兩個方法不像suspend()resume()方法。在這兩個方法中resume()必須在suspend()以前執行。不然線程就會被永遠的掛起,形成死鎖。而LockSupport中的unpark能夠在park以前。安全

package com.montos.lock;
import java.util.concurrent.locks.LockSupport;

public class LockSupportDemo {
	public static Object obj = new Object();
	static ChangeObjectThread t1 = new ChangeObjectThread("t1");
	static ChangeObjectThread t2 = new ChangeObjectThread("t2");

	public static class ChangeObjectThread extends Thread {
		public ChangeObjectThread(String name) {
			super(name);
		}
		@Override
		public void run() {
			synchronized (obj) {
				System.out.println("in " + getName());
				LockSupport.park();
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		t1.start();
		Thread.sleep(100);
		t2.start();
		LockSupport.unpark(t1);
		LockSupport.unpark(t2);
		t1.join();
		t2.join();
	}
}
複製代碼

經過上面的一個小的Demo能夠看出LockSupport的使用方法,在使用方法中也是很簡單的。以上篇幅就對JDK內的併發類進行了講解。大多數用到了CAS無鎖操做。避免線程阻塞的狀況發生。多線程

併發集合

在平常的業務處理中,集合類是咱們使用最多的一個操做。對應的也就是三大類:MapSet以及最後的List。在平常併發量小的狀況下,也許咱們會這麼使用對應的集合操做:併發

//Map集合
private static final Map<String,String> map = Collections.synchronizedMap(new HashMap<String,String>());
//List集合 
private static final List<String> list = Collections.synchronizedList(new ArrayList<String>());
//Set集合
private static final Set<String> set = Collections.synchronizedSet(new HashSet<>());
複製代碼

上面的使用了Collections中的同步方法進行包裝。深刻了解裏面的調用過程,也就是各個方法上面加上了synchronized進行限制。從而達到最後線程安全的目的。若是併發量很大的話,是很會影響性能的,畢竟它使得對應的讀和寫操做都變成了串行。因而有了下面的幾種線程安全類:ide

  • ConcurrentHashMap高性能併發的Map。裏面的操做是跟HashMap是同樣的。可是裏面多了一種結構:Segment(段)。
static class Segment<K,V> extends ReentrantLock implements Serializable {
        private static final long serialVersionUID = 2249069246763182397L;
        final float loadFactor;
        Segment(float lf) { this.loadFactor = lf; }
    }
複製代碼

能夠看出他是繼承了重入鎖進行實現的。爲何會有段的概念在這裏面呢?爲了更好的支持高併發而設計的一個概念。每一個元素在特定的段中,將整個集合分紅許多段進行管理,而高併發的時候,只要對每一個段進行控制就能起到一個同步的做用。高併發

再統計數量大小的時候,ConcurrentHashMap中利用了CounterCell結構體進行統計。每個段中存儲了該段的大小,而後再循環求和出當前的數組大小狀況。post

final long sumCount() {
        CounterCell[] as = counterCells; CounterCell a;
        long sum = baseCount;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;
    }
複製代碼
  • BlockingQueue是一個很是好的多線程共享數據的容器。當隊列爲空的時候,讀線程就會進行等待,相反,若是當前隊列已滿,那麼寫線程就會等待。看到對應的實現類中都會有下面三個成員變量:
/** Main lock guarding all access */
final ReentrantLock lock; //加鎖進行控制訪問

/** Condition for waiting takes */
private final Condition notEmpty;//元素獲取的時候,進行判斷是否爲空進行阻塞

/** Condition for waiting puts */
private final Condition notFull;//元素加入的時候,判斷隊列是否已滿
複製代碼

這上面的三個變量就控制整個阻塞隊列的訪問以及元素的增長。從根本上來講他並不像上面介紹的ConcurrentHashMap訪問性能高,可是咱們須要的是他的多線程訪問共有數據的能力。性能

阻塞隊列的元素獲取方法

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();//獲取可中斷鎖
        try {
            while (count == 0)
                notEmpty.await();//隊列爲空,進行讀線程等待
            return dequeue();
        } finally {
            lock.unlock();
        }
    }
複製代碼
private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();//通知寫線程進行寫入操做
        return x;
    }
複製代碼

阻塞隊列的元素增長方法

public void put(E e) throws InterruptedException {
        checkNotNull(e);//判斷元素是否爲空
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();//獲取可中斷鎖
        try {
            while (count == items.length)
                notFull.await();//隊列滿時,進行寫線程等待
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
複製代碼
private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();//通知讀線程進行操做 
    }
複製代碼
  • ConcurrentLinkedQueue上面談及到的是多線程共享數據的容器,而這個是高併發狀況下使用的。裏面大量的使用了無鎖的操做以及鎖自旋,能夠很好的保證性能問題,有興趣的小夥伴能夠去了解下。

以上談及到的就是JDK中關於併發的一些簡單介紹。在我介紹完成以後,我會對底層的源碼進一步講解,有興趣的小夥伴能夠關注我~

相關文章
相關標籤/搜索