Java并发数据结构

Java并发数据结构

1,并发数据结构介绍

  常用的数据结构是线程不安全的,如平时用到的最多的数据结构类ArrayList、HashMap,HashSet都是**非同步(线程不安全)的,多个线程同时读写,可能会抛出异常或数据错误;传统的Vector、Hashtable等同步(线程安全)**集合性能过差。

  从Java5开始,提出了java.util.concurrent这个包,也相应地提供了一些并发数据结构,这些并发的数据结构,将代替以前的这些结构,给并发编程提供更多的支持。这些并发数据结构可以非为以下两大类:

  • 阻塞式集合:当集合为空或满时,线程要删除或添加数据时需要等待

  • 非阻塞式集合:当集合为空或满时,不等待,返回null或异常

2,List

  • Vector:同步安全,适合多线程并发环境下使用,适合写多读少的环境。
  • ArrayList:性能比较好,但是它是线程不同步的、不安全的。
  • Collections.synchronizedList(List list):基于 synchronized 将一个线程不安全的list变成一个线程安全的list。效率较差。
  • CopyOnWriteArrayList:这个类是基于复制机制的并发列表类,它是一个非阻塞式集合,适合读多写少的环境。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class ListTest {
    public static void main(String[] args) throws InterruptedException {
        // 线程不安全
        List<String> unsafeList = new ArrayList<>();
        // 线程安全
        List<String> safeList1 = Collections.synchronizedList(new ArrayList<>());
        // 线程安全
        CopyOnWriteArrayList<String> safeList2 = new CopyOnWriteArrayList<>();

        ListThread t1 = new ListThread(unsafeList);
        ListThread t2 = new ListThread(safeList1);
        ListThread t3 = new ListThread(safeList2);

        // 分别启动10个t1 t2 t3线程,每个list中将会被插入100条记录
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t1, String.valueOf(i));
            t.start();
        }
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t2, String.valueOf(i));
            t.start();
        }
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t3, String.valueOf(i));
            t.start();
        }
		//等待结果
        Thread.sleep(2000);

        System.out.println("unsafeList.size = " + t1.list.size());
        System.out.println("safeList1.size = " + t2.list.size());
        System.out.println("safeList2.size = " + t3.list.size());
    }
}

class ListThread implements Runnable {
    public List<String> list;

    public ListThread(List<String> list) {
        this.list = list;
    }

    @Override
    public void run() {
        int i = 0;
        while (i < 10) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 把当前线程名加入list中
            list.add(Thread.currentThread().getName());
            i++;
        }
    }
}

从结果可以看到,普通的ArrayList难以承受高并发编程的大量读写。

3,Set

  • HashSet:线程不安全,不适合再多线程环境下使用。
  • Collections.synchronizedSet(Set set):基于 synchronized,效率差。
  • CopyOnWriteArraySet:基于CopyOnWriteArrayList实现,它是一个非阻塞式集合,适合读多写少的环境。
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

public class SetTest {
    public static void main(String[] args) throws InterruptedException {
        // 线程不安全
        Set<String> unsafeSet = new HashSet<>();
        // 线程安全
        Set<String> safeSet1 = Collections.synchronizedSet(new HashSet<>());
        // 线程安全
        CopyOnWriteArraySet<String> safeSet2 = new CopyOnWriteArraySet<>();

        SetThread t1 = new SetThread(unsafeSet);
        SetThread t2 = new SetThread(safeSet1);
        SetThread t3 = new SetThread(safeSet2);

        // 分别启动10个t1 t2 t3线程,每个list中将会被插入100条记录
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t1, String.valueOf(i));
            t.start();
        }
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t2, String.valueOf(i));
            t.start();
        }
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t3, String.valueOf(i));
            t.start();
        }

        Thread.sleep(2000);

        System.out.println("unsafeSet.size = " + t1.set.size());
        System.out.println("safeSet1.size = " + t2.set.size());
        System.out.println("safeSet2.size = " + t3.set.size());
    }
}

class SetThread implements Runnable {
    public Set<String> set;

    public ListThread(Set<String> set) {
        this.set = set;
    }

    @Override
    public void run() {
        int i = 0;
        while (i < 100) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 把当前线程名加入set中
            set.add(Thread.currentThread().getName() + i);
            i++;
        }
    }
}

由于测试的数据量比较小,HashSet的并发读写可能不会出现差错,可以增加测试数据,多测几次。

4,Map

  • HashTable:同步安全,写多读少,性能不大好。
  • HashMap:性能好,但是不安全。
  • Collections.synchronizedMap(Map map):基于 synchronized,效率差。
  • ConcurrentHashMap:读多写少,非阻塞式集合。
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

public class MapTest {
    public static void main(String[] args) throws InterruptedException {
        // 线程不安全
        Map<Integer, String> unsafeMap = new HashMap<>();
        // 线程安全
        Map<Integer, String> safeMap1 = Collections.synchronizedMap(new HashMap<>());
        // 线程安全
        ConcurrentHashMap<Integer, String> safeMap2 = new ConcurrentHashMap<>();

        MapThread t1 = new MapThread(unsafeMap);
        MapThread t2 = new MapThread(safeMap1);
        MapThread t3 = new MapThread(safeMap2);

        // 分别启动10个t1 t2 t3线程,每个map中将会被插入100条记录
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t1, String.valueOf(i));
            t.start();
        }
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t2, String.valueOf(i));
            t.start();
        }
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t3, String.valueOf(i));
            t.start();
        }

        Thread.sleep(2000);

        System.out.println("unsafeMap.size = " + t1.map.size());
        System.out.println("safeMap1.size = " + t2.map.size());
        System.out.println("safeMap2.size = " + t3.map.size());
    }
}

class MapThread implements Runnable {
    public Map<Integer, String> map;

    public ListThread(Map<Integer, String> map) {
        this.map = map;
    }

    @Override
    public void run() {
        int i = 0;
        while (i < 100) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 把当前线程名加入set中
            map.put(i, Thread.currentThread().getName());
            i++;
        }
    }
}

5,Queue & Deque

  • ConcurrentLinkedQueue:非阻塞,安全
  • ArrayBlockingQueue/LinkedBlockingQueue:阻塞,安全。
import java.util.*;
import java.util.concurrent.*;

public class QueueTest {
    public static void main(String[] args) throws InterruptedException {
        // 线程不安全
        Deque<String> unsafeQueue = new ArrayDeque<>();
        // 线程安全
        ConcurrentLinkedDeque<String> safeQueue1 = new ConcurrentLinkedDeque<>();
        // 线程安全
        ArrayBlockingQueue<String> safeQueue2 = new ArrayBlockingQueue<String>(100);

        QueueThread t1 = new QueueThread(unsafeQueue);
        QueueThread t2 = new QueueThread(safeQueue1);
        QueueThread t3 = new QueueThread(safeQueue2);

        // 分别启动10个t1 t2 t3线程,每个list中将会被插入100条记录
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t1, String.valueOf(i));
            t.start();
        }
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t2, String.valueOf(i));
            t.start();
        }
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(t3, String.valueOf(i));
            t.start();
        }

        Thread.sleep(2000);

        System.out.println("unsafeQueue.size = " + t1.queue.size());
        System.out.println("safeQueue1.size = " + t2.queue.size());
        System.out.println("safeQueue2.size = " + t3.queue.size());
    }
}

class QueueThread implements Runnable {
    public Queue<String> queue;

    public QueueThread(Queue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        int i = 0;
        while (i < 10) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 把当前线程名加入queue中
            queue.add(Thread.currentThread().getName());
            i++;
        }
    }
}

  在多线程编程中,采用并发的数据结构,可以避免同时读写引发的数据不一致问题,提高数据结构的使用效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值