一、ThreadLocal
/**
* ThreadLocal
* 就是一個Map。key - 》 Thread.getCurrentThread(). value - 》 執行緒需要儲存的變數。
* ThreadLocal.set(value) -> map.put(Thread.getCurrentThread(), value);
* ThreadLocal.get() -> map.get(Thread.getCurrentThread());
* 記憶體問題 : 在併發量高的時候,可能有記憶體溢位。
* 使用ThreadLocal的時候,一定注意回收資源問題,每個執行緒結束之前,將當前執行緒儲存的執行緒變數一定要刪除 。
* ThreadLocal.remove();
*/
package concurrent.t05;
import java.util.concurrent.TimeUnit;
public class Test_01 {
volatile static String name = "zhangsan";
static ThreadLocal<String> tl = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name);
System.out.println(tl.get());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
name = "lisi";
tl.set("wangwu");
}
}).start();
}
}
複製程式碼
結果:
lisi
null
複製程式碼
二、併發包
1、ConcurrentHashMap
/**
* 併發容器 - ConcurrentMap
*/
package concurrent.t06;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CountDownLatch;
public class Test_01_ConcurrentMap {
public static void main(String[] args) {
// final Map<String, String> map = new Hashtable<>();
// final Map<String, String> map = new ConcurrentHashMap<>();
final Map<String, String> map = new ConcurrentSkipListMap<>();
final Random r = new Random();
Thread[] array = new Thread[100];
final CountDownLatch latch = new CountDownLatch(array.length);
long begin = System.currentTimeMillis();
for(int i = 0; i < array.length; i++){
array[i] = new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 10000; j++){
map.put("key"+r.nextInt(100000), "value"+r.nextInt(100000));
}
latch.countDown();
}
});
}
for(Thread t : array){
t.start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("執行時間為 : " + (end-begin) + "毫秒!");
}
}
複製程式碼
結果:ConcurrentHashMap併發包效率比較高,執行緒安全
2、CopyOnWriteArrayList
/**
* 併發容器 - CopyOnWriteList
*/
package concurrent.t06;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
public class Test_02_CopyOnWriteList {
public static void main(String[] args) {
// final List<String> list = new ArrayList<>();
// final List<String> list = new Vector<>();
final List<String> list = new CopyOnWriteArrayList<>();
final Random r = new Random();
Thread[] array = new Thread[100];
final CountDownLatch latch = new CountDownLatch(array.length);
long begin = System.currentTimeMillis();
for(int i = 0; i < array.length; i++){
array[i] = new Thread(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 1000; j++){
list.add("value" + r.nextInt(100000));
}
latch.countDown();
}
});
}
for(Thread t : array){
t.start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("執行時間為 : " + (end-begin) + "毫秒!");
System.out.println("List.size() : " + list.size());
}
}
複製程式碼
結果:
執行時間為 : 4550毫秒!
List.size() : 100000
複製程式碼
3、ConcurrentLinkedQueue
一個基於連結節點的無界執行緒安全佇列。此佇列按照 FIFO(先進先出)原則對元素進行排序。佇列的頭部 是佇列中時間最長的元素。佇列的尾部 是佇列中時間最短的元素。 新的元素插入到佇列的尾部,佇列獲取操作從佇列頭部獲得元素。當多個執行緒共享訪問一個公共 collection 時,ConcurrentLinkedQueue是一個恰當的選擇。此佇列不允許使用 null 元素。
ConcurrentLinkedQueue由head節點和tair節點組成,每個節點(Node)由節點元素(item)和指向下一個節點的引用(next)組成,節點與節點之間就是通過這個next關聯起來,從而組成一張連結串列結構的佇列。預設情況下head節點儲存的元素為空,tair節點等於head節點。private transient volatile Node<e> tail = head;
複製程式碼
- offer和poll
offer(E e)
將指定元素插入此佇列的尾部。
poll()
獲取並移除此佇列的頭,如果此佇列為空,則返回 null。
public static void main(String[] args) {
ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
queue.offer("哈哈哈");
System.out.println("offer後,佇列是否空?" + queue.isEmpty());
System.out.println("從佇列中poll:" + queue.poll());
System.out.println("pool後,佇列是否空?" + queue.isEmpty());
}
複製程式碼
offer是往佇列新增元素,poll是從佇列取出元素並且刪除該元素
執行結果:
offer後,佇列是否空?false
從佇列中poll:哈哈哈
pool後,佇列是否空?true
複製程式碼
ConcurrentLinkedQueue中的add() 和 offer()完全一樣,都是往佇列尾部新增元素
peek()
獲取但不移除此佇列的頭;如果此佇列為空,則返回 null
public static void main(String[] args) {
ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
queue.offer("哈哈哈");
System.out.println("offer後,佇列是否空?" + queue.isEmpty());
System.out.println("從佇列中peek:" + queue.peek());
System.out.println("從佇列中peek:" + queue.peek());
System.out.println("從佇列中peek:" + queue.peek());
System.out.println("pool後,佇列是否空?" + queue.isEmpty());
}
複製程式碼
執行結果:
offer後,佇列是否空?false
從佇列中peek:哈哈哈
從佇列中peek:哈哈哈
從佇列中peek:哈哈哈
pool後,佇列是否空?false
複製程式碼
remove(Object o)
從佇列中移除指定元素的單個例項(如果存在)
public static void main(String[] args) {
ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
queue.offer("哈哈哈");
System.out.println("offer後,佇列是否空?" + queue.isEmpty());
System.out.println("從佇列中remove已存在元素 :" + queue.remove("哈哈哈"));
System.out.println("從佇列中remove不存在元素:" + queue.remove("123"));
System.out.println("remove後,佇列是否空?" + queue.isEmpty());
}
複製程式碼
remove一個已存在元素,會返回true,remove不存在元素,返回false
執行結果:
offer後,佇列是否空?false
從佇列中remove已存在元素 :true
從佇列中remove不存在元素:false
remove後,佇列是否空?true
複製程式碼
size or isEmpty
size()
返回此佇列中的元素數量
注意:
如果此佇列包含的元素數大於 Integer.MAX_VALUE,則返回 Integer.MAX_VALUE。
需要小心的是,與大多數 collection 不同,此方法不是 一個固定時間操作。由於這些佇列的非同步特性,確定當前的元素數需要進行一次花費 O(n) 時間的遍歷。
所以在需要判斷佇列是否為空時,儘量不要用 queue.size()>0,而是用 !queue.isEmpty()
複製程式碼
isEmpty()的效率要高很多
4、LinkedBlockingQueue
/**
* 併發容器 - LinkedBlockingQueue
* 阻塞容器。
* put & take - 自動阻塞。
* put自動阻塞, 佇列容量滿後,自動阻塞
* take自動阻塞方法, 佇列容量為0後,自動阻塞。
*/
package concurrent.t06;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class Test_04_LinkedBlockingQueue {
final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
final Random r = new Random();
public static void main(String[] args) {
final Test_04_LinkedBlockingQueue t = new Test_04_LinkedBlockingQueue();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
t.queue.put("value"+t.r.nextInt(1000));
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "producer").start();
for(int i = 0; i < 3; i++){
new Thread(new Runnable() {
@Override
public void run() {
while(true){
try {
System.out.println(Thread.currentThread().getName() +
" - " + t.queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "consumer"+i).start();
}
}
}
複製程式碼
結果:
consumer0 - value221
consumer1 - value291
consumer0 - value386
consumer2 - value56
consumer1 - value336
consumer0 - value522
consumer2 - value731
consumer1 - value757
consumer0 - value497
consumer2 - value696
consumer1 - value589
複製程式碼
5、ArrayBlockingQueue
/**
* 併發容器 - ArrayBlockingQueue
* 有界容器。add方法當容量不足的時候有阻塞能力
* put方法在容量不足的時候阻塞
* offer方法
* 單引數offer方法,不阻塞,容量不足的時候,返回false,當前新增資料操作放棄
* 三引數offer方法(offer(value,times,timeunit)),容量不足的時候阻塞times時長(單位為timeunit),
* 如果在阻塞時長內,有容量空閒,新增資料返回true,如果阻塞時長範圍內無容量空閒,放棄新增資料返回false
*/
package concurrent.t06;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class Test_05_ArrayBlockingQueue {
final BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
public static void main(String[] args) {
final Test_05_ArrayBlockingQueue t = new Test_05_ArrayBlockingQueue();
for(int i = 0; i < 5; i++){
System.out.println("add method : " + t.queue.add("value"+i));
/*try {
t.queue.put("put"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("put method : " + i);*/
// System.out.println("offer method : " + t.queue.offer("value"+i));
// try {
// System.out.println("offer method : " +
// t.queue.offer("value"+i, 1, TimeUnit.SECONDS));
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
System.out.println(t.queue);
}
}
複製程式碼
結果:
add method : true
add method : true
Exception in thread "main" java.lang.IllegalStateException: Queue fulladd method : true
at java.util.AbstractQueue.add(Unknown Source)
at java.util.concurrent.ArrayBlockingQueue.add(Unknown Source)
at concurrent.t06.Test_05_ArrayBlockingQueue.main(Test_05_ArrayBlockingQueue.java:22)
複製程式碼
6、DelayQueue
/**
* 併發容器 - DelayQueue
* 無界容器。
* 常用於計劃任務,如定時傳送郵件
*/
package concurrent.t06;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class Test_06_DelayQueue {
static BlockingQueue<MyTask_06> queue = new DelayQueue<>();
public static void main(String[] args) throws InterruptedException {
long value = System.currentTimeMillis();
MyTask_06 task1 = new MyTask_06(value + 2000);
MyTask_06 task2 = new MyTask_06(value + 1000);
MyTask_06 task3 = new MyTask_06(value + 3000);
MyTask_06 task4 = new MyTask_06(value + 2500);
MyTask_06 task5 = new MyTask_06(value + 1500);
queue.put(task1);
queue.put(task2);
queue.put(task3);
queue.put(task4);
queue.put(task5);
System.out.println(queue);
System.out.println(value);
for(int i = 0; i < 5; i++){
System.out.println(queue.take());
}
}
}
class MyTask_06 implements Delayed {
private long compareValue;
public MyTask_06(long compareValue){
this.compareValue = compareValue;
}
/**
* 比較大小。自動實現升序
* 建議和getDelay方法配合完成。
* 如果在DelayQueue是需要按時間完成的計劃任務,必須配合getDelay方法完成。
*/
@Override
public int compareTo(Delayed o) {
return (int)(this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
/**
* 獲取計劃時長的方法。
* 根據引數TimeUnit來決定,如何返回結果值。
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(compareValue - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public String toString(){
return "Task compare value is : " + this.compareValue;
}
}
複製程式碼
結果:
[Task compare value is : 1524846737601, Task compare value is : 1524846738101, Task compare value is : 1524846739601, Task compare value is : 1524846739101, Task compare value is : 1524846738601]
1524846736601
Task compare value is : 1524846737601
Task compare value is : 1524846738101
Task compare value is : 1524846738601
Task compare value is : 1524846739101
Task compare value is : 1524846739601
複製程式碼
7、LinkedTransferQueue
/**
* 併發容器 - LinkedTransferQueue
* 轉移佇列
* add - 佇列會儲存資料,不做阻塞等待。
* transfer - 是TransferQueue的特有方法。必須有消費者(take()方法的呼叫者)。
* 如果沒有任意執行緒消費資料,transfer方法阻塞。一般用於處理即時訊息。
*/
package concurrent.t06;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TransferQueue;
public class Test_07_TransferQueue {
TransferQueue<String> queue = new LinkedTransferQueue<>();
public static void main(String[] args) {
final Test_07_TransferQueue t = new Test_07_TransferQueue();
/*new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " thread begin " );
System.out.println(Thread.currentThread().getName() + " - " + t.queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "output thread").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
t.queue.transfer("test string");
} catch (InterruptedException e) {
e.printStackTrace();
}*/
new Thread(new Runnable() {
@Override
public void run() {
try {
t.queue.transfer("test string");
// t.queue.add("test string");
System.out.println("add ok");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " thread begin " );
System.out.println(Thread.currentThread().getName() + " - " + t.queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "output thread").start();
}
}
複製程式碼
結果:
output thread thread begin
output thread - test string
add ok
複製程式碼
8、SynchronousQueue
同步佇列,是一個容量為 0 的佇列。是一個特殊的 TransferQueue。必須有消費者消費。
add方法,無阻塞。若沒有消費執行緒阻塞等待資料,則丟擲異常
put方法,有阻塞,若沒有消費執行緒阻塞等待資料,則阻塞。
/**
* 併發容器 - SynchronousQueue
*/
package concurrent.t06;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class Test_08_SynchronusQueue {
BlockingQueue<String> queue = new SynchronousQueue<>();
public static void main(String[] args) {
final Test_08_SynchronusQueue t = new Test_08_SynchronusQueue();
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " thread begin " );
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " - " + t.queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "output thread").start();
/*try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
// t.queue.add("test add");
try {
t.queue.put("test put");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " queue size : " + t.queue.size());
}
}
複製程式碼
結果:
output thread thread begin
output thread - test put
main queue size : 0
複製程式碼