Java多线程——安全线程问题(ConcerrentHashMap、CopyOnWriteArrayList 、CopyOnWriteArraySet、ArrayBlockingQueue)

线程安全的集合

 

 

问题的由来:

复制代码
 1 import java.util.ArrayList;
 2 
 3  
 4 
 5 public class Demo05 {
 6 
 7     public static void main(String[] args) {
 8 
 9 //        创建ArrayList
10 
11         ArrayList<String> arrayList=new ArrayList<>();
12 
13 //        创建线程
14 
15         for (int i=0;i<=10;i++){
16 
17             int temp = i;
18 
19             new Thread(new Runnable() {
20 
21                 @Override
22 
23                 public void run() {
24 
25                     for (int j = 0;j<=10;j++){
26 
27                         arrayList.add(Thread.currentThread().getName()+"==="+temp+"==="+j);
28 
29                         System.out.println(arrayList.toString());
30 
31                     }
32 
33                 }
34 
35             }).start();
36 
37         }
38 
39  
40 
41     }
42 
43 }
复制代码

 

问题:并发异常。

 

 

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();

        }

    }

}
复制代码

 

posted @   逃逸线LinesOfFlight  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示