线程安全的集合
问题的由来:
import java.util.ArrayList;
public class Demo05 {
public static void main(String[] args) {
// 创建ArrayList
ArrayList<String> arrayList=new ArrayList<>();
// 创建线程
for (int i=0;i<=10;i++){
int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0;j<=10;j++){
arrayList.add(Thread.currentThread().getName()+"==="+temp+"==="+j);
System.out.println(arrayList.toString());
}
}
}).start();
}
}
}
问题:并发异常。
Collections中的工具方法
JDK1.2提供,接口统一、维护性高,性能未提升,仍是用synchronized实现
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class Demo05 {
public static void main(String[] args) {
方法一 、创建ArrayList
// ArrayList<String> arrayList=new ArrayList<>();
使用Collections中的线程安全方法转化为线程安全的集合的
// 方法二 、 List<String> synList= Collections.synchronizedList(arrayList);
CopyOnWriteArrayList<String> arrayList=new CopyOnWriteArrayList<>();
// 创建线程
for (int i=0;i<=10;i++){
int temp = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0;j<=10;j++){
arrayList.add(Thread.currentThread().getName()+"==="+temp+"==="+j);
System.out.println(arrayList.toString());
}
}
}).start();
}
}
}
CopyOnWriteArrayList集合
线程安全的ArrayList集合,加强版的读写分离
写有锁,读无锁,读写之间不阻塞,优于读写锁
写入时,先复制一个容器副本,再添加新的元素,最后替换引用
(简单来说就是牺牲内存空间来换取安全性)
使用方法与ArrayList无差异
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo06 {
public static void main(String[] args) {
// 1.创建集合
CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<>();
// 2.使用多线程操作
ExecutorService es = Executors.newFixedThreadPool(5);
// 3.提交任务
for (int i=1;i<=5;i++){
es.submit(new Runnable() {
@Override
public void run() {
list.add(Thread.currentThread().getName()+"..."+new Random().nextInt(1000));
}
});
}
// 4.关闭线程池
es.shutdown();
while(!es.isTerminated()){ }
// 5.打印结果
System.out.println("元素个数:"+list.size());
for (String s : list){
System.out.println(s);
}
}
}
CopyOnWriteArraySet
底层的实现逻辑为CopyOnWriteArrayList,但是添加元素为addIfAbsent()方法,会遍历数组
如存在元素,则不添加,扔掉副本。
重复依据,copyOf()方法。
代码示例:
import java.util.concurrent.CopyOnWriteArraySet;
public class Demo07 {
public static void main(String[] args) {
CopyOnWriteArraySet<String> set=new CopyOnWriteArraySet<>();
// 2.添加元素
set.add("pingguo");
set.add("huawei");
set.add("xiaomi");
// 3.打印
System.out.println("元素个数:"+set.size());
System.out.println(set.toString());
}
}
Queue(Collection的子接口)队列
表示FIFO(FIRST IN FIRST OUT)先进先出
常用方法
Boolean offer(E e) 顺序添加一个元素(达到上限后再添加则返回false)
E poll() 获得第一个元素并移除(若无元素返回null)
E peak() 获得第一个元素但不移除(若无元素返回null)
代码示例:
import java.sql.SQLOutput;
import java.util.LinkedList;
import java.util.Queue;
public class Demo08 {
public static void main(String[] args) {
// 1.创建队列
Queue<String> queue=new LinkedList<>();
// 2.入队
queue.offer("苹果");
queue.offer("西瓜");
queue.offer("榴莲");
// 3.出队
System.out.println(queue.peek());
System.out.println("========");
System.out.println("元素个数:"+queue.size());
int size=queue.size();
for (int i=0;i<=3;i++){
System.out.println(queue.poll());
}
System.out.println("出对完毕:"+queue.size());
}
}
注意:以上代码是由单线程的集合实现Linklist,非安全线程。
Queue相关的安全线程
ConcurrentLinkedQueue(无界队列)
线程安全、可读写的队列,高并发下性能最好的队列
无锁、CAS比较交换算法,修改方法包含三个关键参数<V,E,N>
其中V是读取出来的量,即要更新的变量;E判断原值和读取出来的值是一样的以确保在读取过程中不会被修改;V,新值。只有V==E,V=N.否则表示已经更新过,取消当前操作。
代码示例:
import java.util.concurrent.ConcurrentLinkedQueue;
public class Demo09 {
public static void main(String[] args) throws Exception{
// 1.创建安全队列
ConcurrentLinkedQueue<Integer> queue=new ConcurrentLinkedQueue<>();
// 2.入队操作
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<=5;i++){
queue.offer(i);
}
}
});
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
for (int i=6;i<=10;i++){
queue.offer(i);
}
}
});
// 3.启动线程
thread.start();
thread2.start();
thread.join();
thread2.join();
// 4.出队操作
int j=queue.size();
for (int i=0;i<=j;i++){
System.out.println(queue.poll());
}
}
}
BlockingQueue
Queue的子接口,阻塞的队列增加两个线程状态为无限期等待的方法。
Void put(E e)//将指定元素插入此队列中,如果没有可用空间,则等待
E take()//获取并移除此队列头部元素,如果没有则等待
(可解决线程中类似的生产者消费者问题)
代码示例:
import java.util.concurrent.ArrayBlockingQueue;
public class Demo10 {
public static void main(String[] args) throws Exception{
ArrayBlockingQueue<String> queue=new ArrayBlockingQueue<>(5);
queue.put("a");
queue.put("b");
queue.put("c");
queue.put("d");
queue.put("f");
System.out.println("已添加五个元素");
// ArrayBlockingQueue,只能容纳五个元素,再添加元素则阻塞
// 若移除其中一个元素则可以继续往下执行
queue.take();
queue.put("h");
System.out.println("添加第六个元素");
// System.out.println(queue.toString());
}
}
再次实现生产者消费者的问题:
import java.util.concurrent.ArrayBlockingQueue;
public class Demo11 {
public static void main(String[] args) {
// 1.创建队列
ArrayBlockingQueue<Integer> abq=new ArrayBlockingQueue<>(6);
// 2.创建两个线程
Thread t1=new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<=30;i++){
try {
abq.put(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"生产了第"+i+"号面包");
}
}
},"ll");
Thread t2=new Thread(new Runnable() {
@Override
public void run() {
for (int i=0;i<=30;i++){
try {
Integer num=abq.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"消费了第"+i+"号面包");
}
}
},"zz");
// 启动线程
t1.start();
t2.start();
}
}
ConcurrentHashMap
JDK1.7以及之前,初始容量为十六段,最大并发量也是16,枷锁方式为对每一个小段加锁,当两个或更多的对象加入到同一个Segment时需要HashMap。使用方式与HashMap无差异。JDK1.8之后使用的是无锁算法即CAS.效率更高。
代码示例:
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
import java.util.concurrent.ConcurrentHashMap;
public class Demo12 {
public static void main(String[] args) {
// 1.创建集合
ConcurrentHashMap<String,String> hashMap=new ConcurrentHashMap<String,String>();
// 2.使用多线程添加数据
for (int i=0;i<=5;i++){
new Thread(new Runnable() {
@Override
public void run() {
for (int k=0;k<=10;k++){
hashMap.put(Thread.currentThread().getName()+"---"+k,k+"<-");
System.out.println(hashMap.toString());
}
}
}).start();
}
}
}