一、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 元素。
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