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

线程安全的集合

问题的由来:

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

        }

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逃逸线LOF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值