并发集合框架

目录

前言

正文

1.集合框架结构 

2. ConcurrentHashMap 

(1)验证 HashMap 不是线程安全的 

(2)验证 Hashtable 是线程安全的 

(3)验证 Hashtable 不支持并发 remove 操作

(4)验证 ConcurrentHashMap 线程安全特性 

3.ConcurrentSkipListMap 

4.ConcurrentSkipListSet 

5.ConcurrentLinkedQueue 

6.ConcurrentLinkedDeque

7.CopyOnWriteArrayList 

8.CopyOnWriteArrarySet

9.SynchronousQueue 

总结


前言

并发集合框架是为了在多线程环境下提供高效和线程安全的数据结构而设计的。Java 的并发集合框架提供了一组线程安全的集合类,可以在多线程应用程序中使用,以解决并发访问集合时可能出现的竞态条件和线程安全问题。


正文

对于高度并发的应用程序,使用并发集合可以显著提高性能。与传统的同步集合相比,它们提供了更高的并行度和更好的扩展性。并发集合框架中的数据结构经过优化,允许多个线程同时对其进行读写,以提高并发访问的性能。

Java 并发集合框架包括 ConcurrentHashMapConcurrentLinkedQueueConcurrentSkipListMap 等。这些集合类可以在高并发读写场景中大大简化编程和提高性能。

1.集合框架结构 

JAVA 语言中的集合框架父接口是 Iterable,从这个接口向下一一继承就可以得到完整的 Java 集合框架结构。集合框架的继承与实现关系相当复杂,简化的集合框架接口结构如图所示: 

可以发现出现 3 个继承分支(List、Set、Queue)的结构是接口 Collection,它是集合框架的主要功能抽象,另一个接口是 Map ,与集合 Collection 区分开来。虽然这些集合框架的知识点很重要,但我们主要对这些接口继续向下衍生的并发集合框架进行了解。

2. ConcurrentHashMap 

类 ConcurrentHashMap 是支持并发操作的对象。 

(1)验证 HashMap 不是线程安全的 

创建测试用例

package org.example.Collection;


import java.util.HashMap;

public class Concurrent_HashMap {
    static class MyService{
        public  HashMap map = new HashMap();
        public void testMethod(){
            for (int i = 0; i < 50000; i++) {
                try {
                    if (!Thread.currentThread().isInterrupted()) {
                        map.put(Thread.currentThread().getName()+" "+(i+1),Thread.currentThread().getName()+" "+(i+1));
                        System.out.println(Thread.currentThread().getName()+" "+(i+1));
                    }
                } catch (Exception e) {
                    System.err.println("Error: "+e.getMessage());
                    Thread.currentThread().getThreadGroup().interrupt();
                    System.exit(0);

                }
            }
        }
    }
    static class MyThread extends Thread{
        private MyService service;

        public MyThread(MyService service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.testMethod();
        }
    }

    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThread a = new MyThread(myService);
        MyThread b = new MyThread(myService);
        a.start();
        b.start();
    }
}

运行程序结果如图:

程序运行后有很小的概率出现异常,说明 HashMap不能被多个线程操作,也就证明了 HashMap不是线程安全的。

(2)验证 Hashtable 是线程安全的 

由于 HashMap 不适合在多线程的情况下使用。如果想在多线程环境中使用 key-value 的数据结构,可以使用 Hashtable 类,其内部的 put 和 get 方法都是同步的。 

package org.example.Collection;


import java.util.Hashtable;

public class Concurrent_Hashtable {
    static class MyService{
        public Hashtable table = new Hashtable();
        
        public void testMethod(){
            for (int i = 0; i < 50000; i++) {
                try {
                    if (!Thread.currentThread().isInterrupted()) {
                        table.put(Thread.currentThread().getName()+" "+(i+1),Thread.currentThread().getName()+" "+(i+1));
                        System.out.println(Thread.currentThread().getName()+" "+(i+1));
                    }
                } catch (Exception e) {
                    System.err.println("Error: "+e.getMessage());
                    Thread.currentThread().getThreadGroup().interrupt();
                    System.exit(0);

                }
            }
        }
    }
    static class MyThread extends Thread{
        private MyService service;

        public MyThread(MyService service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.testMethod();
        }
    }

    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThread a = new MyThread(myService);
        MyThread b = new MyThread(myService);
        a.start();
        b.start();
    }
}

运行结果如图 

程序运行正确,每个线程添加 5000 个元素,说明 Hashtable 类在多线程环境中执行 put 操作不会报错,是线程安全的类。 

但是,多个线程分别调用分别调用该类的 iterator() 方法返回 Iterator 对象,并调用 next() 方法取得元素,在执行 remove() 方法时会出现 ConcurrentModificationException 异常,也就是说 Hashtable 并不支持 Iterator 并发删除。 

(3)验证 Hashtable 不支持并发 remove 操作

新建测试用例

package org.example.Collection;


import java.util.Hashtable;
import java.util.Iterator;

public class Concurrent_Hashtable {
    static class MyService {
        public Hashtable table = new Hashtable();

        public MyService() {
            for (int i = 0; i < 100000; i++) {
                table.put(Thread.currentThread().getName() + i + 1, "abc");
            }
        }

        public void testMethod() {
            Iterator iterator = table.keySet().iterator();
            while (iterator.hasNext()){
                try {
                    Object object = iterator.next();
                    iterator.remove();
                    System.out.println(table.size()+" "+Thread.currentThread().getName());
                } catch (Exception e) {
                    e.printStackTrace();
                    System.exit(0);
                }
            }
        }
    }

    static class MyThread extends Thread {
        private MyService service;

        public MyThread(MyService service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.testMethod();
        }
    }

    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThread a = new MyThread(myService);
        MyThread b = new MyThread(myService);
        a.start();
        b.start();
    }
}

运行结果如图

程序运行后出现异常,说明 Hashtable 在获得 Iterator 对象后,不允许多个线程同时执行 remove 删除操作,否则出现 java.util.ConcurrentModificationException 异常。

根据上面的测试可以分析出,Hashtable 类支持多线程环境下的 put 添加操作,却不支持 remove 删除操作,但 ConcurrentHashMap 支持这两个操作。

(4)验证 ConcurrentHashMap 线程安全特性 

ConcurrentHashMap 是 JDK 并发包中提供的支持并发操作的 Map 对象。其继承与实现信息如图。 

新建测试用例

package org.example.Collection;


import java.util.Hashtable;
import java.util.concurrent.ConcurrentHashMap;

public class Concurrent_HashMap {
    static class MyService{
            public ConcurrentHashMap map = new ConcurrentHashMap();
        public void testMethod(){
            for (int i = 0; i < 50000; i++) {
                try {
                    if (!Thread.currentThread().isInterrupted()) {
                        map.put(Thread.currentThread().getName()+" "+(i+1),Thread.currentThread().getName()+" "+(i+1));
                        System.out.println(Thread.currentThread().getName()+" "+(i+1));
                    }
                } catch (Exception e) {
                    System.err.println("Error: "+e.getMessage());
                    Thread.currentThread().getThreadGroup().interrupt();
                    System.exit(0);

                }
            }
        }
    }
    static class MyThread extends Thread{
        private MyService service;

        public MyThread(MyService service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.testMethod();
        }
    }

    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThread a = new MyThread(myService);
        MyThread b = new MyThread(myService);
        a.start();
        b.start();
    }
}

运行结果如图:

此运行结果说明类 ConcurrentHashMap 支持在多线程环境中执行 put 操作。 

并且支持并发 remove 操作;

package org.example.Collection;


import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;

public class Concurrent_Hashtable {
    static class MyService {
        public ConcurrentHashMap map = new ConcurrentHashMap();

        public MyService() {
            for (int i = 0; i < 100000; i++) {
                map.put(Thread.currentThread().getName() + i + 1, "abc");
            }
        }

        public void testMethod() {
            Iterator iterator = map.keySet().iterator();
            while (iterator.hasNext()){
                try {
                    Object object = iterator.next();
                    iterator.remove();
                    System.out.println(map.size()+" "+Thread.currentThread().getName());
                } catch (Exception e) {
                    e.printStackTrace();
                    System.exit(0);
                }
            }
        }
    }

    static class MyThread extends Thread {
        private MyService service;

        public MyThread(MyService service) {
            this.service = service;
        }

        @Override
        public void run() {
            service.testMethod();
        }
    }

    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThread a = new MyThread(myService);
        MyThread b = new MyThread(myService);
        a.start();
        b.start();
    }
}

运行结果如图:

运行结果是成功的,说明类 ConcurrentHashMap 在功能上比 Hashtable 更完善,支持并发情况下的 put 和 remove 操作。

ConcurrentHashMap  不支持排序,LinkedHashMap 支持 key 排序,但不支持并发。那么,如果出现这种及要求并发安全,又要求排序的情况,我们就可以使用类 ConcurrentSkipListMap。 

3.ConcurrentSkipListMap 

ConcurrentSkipListMap 支持排序。 

package org.example.Collection;

import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;

public class Concurrent_SkipListMap {
    static class Userinfo implements Comparable<Userinfo>{
        private int id;
        private String username;

        public Userinfo(int id, String username) {
            this.id = id;
            this.username = username;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        @Override
        public int compareTo(Userinfo o) {
            if (this.getId() > o.getId()){
                return 1;
            }else {
                return -1;
            }
        }
    }
    static class MyService{
        private ConcurrentSkipListMap<Userinfo,String> map = new ConcurrentSkipListMap<>();

        public MyService() {
            Userinfo userinfo1 = new Userinfo(1,"userinfo1");
            Userinfo userinfo3 = new Userinfo(3,"userinfo3");
            Userinfo userinfo5 = new Userinfo(5,"userinfo5");
            Userinfo userinfo2 = new Userinfo(2,"userinfo2");
            Userinfo userinfo4 = new Userinfo(4,"Userinfo4");
            map.put(userinfo1,"u1");
            map.put(userinfo3,"u3");
            map.put(userinfo5,"u5");
            map.put(userinfo2,"u2");
            map.put(userinfo4,"u4");
        }
        public void testMethod(){
            Map.Entry<Userinfo,String> entry = map.pollFirstEntry();
            System.out.println("map.size()="+map.size());
            Userinfo userinfo = entry.getKey();
            System.out.println(
                    userinfo.getId()+" "+userinfo.getUsername()+" "
                            +map.get(userinfo)+" "+entry.getValue()
            );
        }
    }
    static class MyThread extends Thread{
        private MyService myService;

        public MyThread(MyService myService) {
            this.myService = myService;
        }

        @Override
        public void run() {
            myService.testMethod();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
        MyThread a1 = new MyThread(service);
        MyThread a2 = new MyThread(service);
        MyThread a3 = new MyThread(service);
        MyThread a4 = new MyThread(service);
        MyThread a5 = new MyThread(service);
        a1.start();
        Thread.sleep(1000);
        a2.start();
        Thread.sleep(1000);
        a3.start();
        Thread.sleep(1000);
        a4.start();
        Thread.sleep(1000);
        a5.start();
    }

}

运行结果如图:

控制台打印出 null 值是使用 polldFirstEntry 方法将当前的 Entry 对象从类 ConcurrentSkipListMap 中删除造成的。

4.ConcurrentSkipListSet 

类 ConcurrentSkipListSet 支持排序且不允许元素重复。 

import java.util.Map;
import java.util.concurrent.ConcurrentSkipListMap;

public class Concurrent_SkipListSet {
    static class Userinfo implements Comparable<Userinfo>{
        private int id;
        private String username;

        public Userinfo() {
        }

        public Userinfo(int id, String username) {
            this.id = id;
            this.username = username;
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        @Override
        public int compareTo(Userinfo o) {
            if (this.getId() > o.getId()){
                return 1;
            }else {
                return -1;
            }
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime* result+id;
            result = prime * result +((username == null)?0:username.hashCode());
            return result;
        }
        @Override
        public boolean equals(Object obj) {
            if (this == obj){
                return true;
            }
            if (obj == null){
                return false;
            }
            if (getClass() != obj.getClass()){
                return false;
            }
            Userinfo orther = (Userinfo) obj;
            if (id != orther.id){
                return false;
            }
            if (username == null){
                if (orther.username != null){
                    if (orther.username != null){
                        return false;
                    }
                }
            }else if (!username.equals(orther.username)){
                return false;
            }
            return true;
        }
    }
    static class MyService{
        private ConcurrentSkipListMap<Concurrent_SkipListMap.Userinfo,String> map = new ConcurrentSkipListMap<>();

        public MyService() {
            Concurrent_SkipListMap.Userinfo userinfo1 = new Concurrent_SkipListMap.Userinfo(1,"userinfo1");
            Concurrent_SkipListMap.Userinfo userinfo3 = new Concurrent_SkipListMap.Userinfo(3,"userinfo3");
            Concurrent_SkipListMap.Userinfo userinfo5 = new Concurrent_SkipListMap.Userinfo(5,"userinfo5");
            Concurrent_SkipListMap.Userinfo userinfo2 = new Concurrent_SkipListMap.Userinfo(2,"userinfo2");
            Concurrent_SkipListMap.Userinfo userinfo4 = new Concurrent_SkipListMap.Userinfo(4,"Userinfo4");
            map.put(userinfo1,"u1");
            map.put(userinfo3,"u3");
            map.put(userinfo5,"u5");
            map.put(userinfo2,"u2");
            map.put(userinfo4,"u4");
        }
        public void testMethod(){
            Map.Entry<Concurrent_SkipListMap.Userinfo,String> entry = map.pollFirstEntry();
            System.out.println("map.size()="+map.size());
            Concurrent_SkipListMap.Userinfo userinfo = entry.getKey();
            System.out.println(
                    userinfo.getId()+" "+userinfo.getUsername()+" "
                            +map.get(userinfo)+" "+entry.getValue()
            );
        }
    }
    static class MyThread extends Thread{
        private MyService myService;

        public MyThread(MyService myService) {
            this.myService = myService;
        }

        @Override
        public void run() {
            myService.testMethod();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
        MyThread a1 = new MyThread(service);
        MyThread a2 = new MyThread(service);
        MyThread a3 = new MyThread(service);
        MyThread a4 = new MyThread(service);
        MyThread a5 = new MyThread(service);
        a1.start();
        Thread.sleep(1000);
        a2.start();
        Thread.sleep(1000);
        a3.start();
        Thread.sleep(1000);
        a4.start();
        Thread.sleep(1000);
        a5.start();
    }
}

运行结果如图:

从结果来看,排序成功,并且不支持重复元素。  

5.ConcurrentLinkedQueue 

ConcurrentLinkedQueue 提供了并发环境下的队列操作。

package org.example.Collection;


import java.util.concurrent.ConcurrentLinkedQueue;

public class Concurrent_LinkedQueue {
    static class MyService{
        public ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
    }
    static class MyThread extends Thread{
        private MyService myService;

        public MyThread(MyService myService) {
            this.myService = myService;
        }

        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                myService.queue.add(Thread.currentThread().getName()+(i+1));
            }
        }
    }

    public static void main(String[] args) {
        try {
            MyService service = new MyService();
            MyThread a = new MyThread(service);
            MyThread b = new MyThread(service);
            a.setName("a");
            b.setName("b");
            a.start();
            b.start();
            a.join();
            b.join();
            System.out.println(service.queue.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

方法运行结果如图:

  • 方法 poll() 没有获得数据时返回 null,获得数据时则移除表头,并将表头进行返回。
  • 方法 element() 没有获得数据时出现 NoSuch'ElementException 异常,获得数据时则不移除表头,并将表头进行返回。
  • 方法 peek() 没有获得数据时返回 null,获得数据时则不移除表头,并将表头进行返回。

测试 main 方法 poll():

public static void main(String[] args) {
       MyService service = new MyService();
        System.out.println(service.queue.poll());
    }

运行结果如图:

修改 main 方法 poll():


    public static void main(String[] args) {
        MyService service = new MyService();
        service.queue.add("a");
        service.queue.add("b");
        service.queue.add("c");
        System.out.println("begin size:" + service.queue.size());
        System.out.println(service.queue.poll());
        System.out.println("    end size:" + service.queue.size());

    }

运行结果:

修改 main 方法  elemen():

    public static void main(String[] args) {
        MyService service = new MyService();
        /*service.queue.add("a");
        service.queue.add("b");
        service.queue.add("c");*/
        System.out.println("begin size:" + service.queue.size());
        System.out.println(service.queue.element());
        System.out.println("    end size:" + service.queue.size());

    }

运行结果如图:

出现没有元素的异常。

修改 main 的代码如下 elemen():

public static void main(String[] args) {
        MyService service = new MyService();
        service.queue.add("a");
        service.queue.add("b");
        service.queue.add("c");
        System.out.println("begin size:" + service.queue.size());
        System.out.println(service.queue.element());
        System.out.println("    end size:" + service.queue.size());

    }

运行结果如图:

可见,打印出队列中元素的个数为 3 。 

修改 main 方法如下 peek():

public static void main(String[] args) {
        MyService service = new MyService();
        /*service.queue.add("a");
        service.queue.add("b");
        service.queue.add("c");*/
        System.out.println("begin size:" + service.queue.size());
        System.out.println(service.queue.peek());
        System.out.println("    end size:" + service.queue.size());

    }

运行结果如图:

修改 main 方法如下 peek():

    public static void main(String[] args) {
        MyService service = new MyService();
        service.queue.add("a");
        service.queue.add("b");
        service.queue.add("c");
        System.out.println("begin size:" + service.queue.size());
        System.out.println(service.queue.peek());
        System.out.println("    end size:" + service.queue.size());

    }

运行结果如图:

6.ConcurrentLinkedDeque

ConcurrentLinkedQueue 仅支持对队列头进行操作,类 ConcurrentLinkedDeque 支持对队列头和列尾双向进行操作。

创建测试用例:

package org.example.Collection;

import java.util.concurrent.ConcurrentLinkedDeque;

public class Concurrent_LinedQueue {
    static class MyService {
        public ConcurrentLinkedDeque deque = new ConcurrentLinkedDeque();

        public MyService() {
            for (int i = 0; i < 4; i++) {
                deque.add("string" + (i + 1));
            }
        }
    }

    static class MyThreadF extends Thread {
        private MyService service;

        public MyThreadF(MyService service) {
            this.service = service;
        }

        @Override
        public void run() {
            System.out.println("value=" + service.deque.pollFirst()
                    + " queue.size()=" + service.deque.size());
        }
    }static class MyThreadL extends Thread {
        private MyService service;

        public MyThreadL(MyService service) {
            this.service = service;
        }

        @Override
        public void run() {
            System.out.println("value=" + service.deque.pollLast()
                    + " queue.size()=" + service.deque.size());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
        MyThreadF aF = new MyThreadF(service);
        MyThreadF bF = new MyThreadF(service);
        MyThreadL aL = new MyThreadL(service);
        MyThreadL bL = new MyThreadL(service);
        aF.start();
        Thread.sleep(1000);
        aL.start();
        Thread.sleep(1000);
        bF.start();
        Thread.sleep(1000);
        bL.start();
    }
}

运行结果如图:

可见,数据成功从列头和列尾弹出,最后队列中的元素个数为 0。

7.CopyOnWriteArrayList 

由于 ArraryList 为非线程安全的。如果想在并发环境下实现线程安全,我们可以使用类 CopyOnWriteArraryList。

import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOn_WriteArraryList {
    static class MyService {
        public static CopyOnWriteArrayList list = new CopyOnWriteArrayList();
    }

    static class MyThread extends Thread {
        private MyService service;

        public MyThread(MyService service) {
            this.service = service;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                service.list.add("anyString");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
        MyThread[] aArray = new MyThread[100];
        for (int i = 0; i < aArray.length; i++) {
            aArray[i] = new MyThread(service);
        }
        for (int i = 0; i < aArray.length; i++) {
            aArray[i].start();
        }
        Thread.sleep(3000);
        System.out.println(service.list.size());
        System.out.println("可以随机取得的值:" + service.list.get(5));
    }
}

程序运行结果如下:

8.CopyOnWriteArrarySet

与类 CopyOnWriteArraryList 配套的还有一个类——CopyOnWriteArrarySet,它也可以解决多环境下 HashSet 不安全的问题。

package org.example.Collection;

import java.util.concurrent.CopyOnWriteArraySet;

public class CopyOn_WriteArrarySet {
    static class MyService {
        public static CopyOnWriteArraySet set = new CopyOnWriteArraySet();
    }

    static class MyThread extends Thread {
        private MyService service;

        public MyThread(MyService service) {
            this.service = service;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                service.set.add(Thread.currentThread().getName()+"anything"+(i+1));
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
        MyThread[] aArray = new MyThread[100];
        for (int i = 0; i < aArray.length; i++) {
            aArray[i] = new MyThread(service);
        }
        for (int i = 0; i < aArray.length; i++) {
            aArray[i].start();
        }
        Thread.sleep(3000);
        System.out.println(service.set.size());
    }
}

运行结果如下:

运行结果说明 100 个线程中,每个线程向队列添加100个元素,最终元素个数是正确的,呈线程安全的效果。

ConcurrentSkipListSet 是线程安全的有序集合, CopyOnWriteArrarySet 是线程安全的无序集合。我们可以将 CopyOnWriteArrarySet 理解成线程安全的 HashSet 。

9.SynchronousQueue 

A blocking queue其中每个插入操作必须等待另一个线程相应的删除操作,反之亦然。 同步队列没有任何内部容量,甚至没有一个容量。 你不能peek在同步队列,因为一个元素,当您尝试删除它才存在; 您无法插入元素(使用任何方法),除非另有线程正在尝试删除它; 你不能迭代,因为没有什么可以迭代。 队列的头部是第一个排队的插入线程尝试添加到队列中的元素; 如果没有这样排队的线程,那么没有元素可用于删除,并且poll()将返回null 。 为了其他Collection方法(例如contains )的目的, SynchronousQueue充当空集合。 此队列不允许null元素。 

public static void main(String[] args) {
        try {
            SynchronousQueue queue = new SynchronousQueue();
            System.out.println("step1");
            queue.put("anyString");
            System.out.println("step2");
            System.out.println(queue.take());
            System.out.println("step3");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

由于数据并没有被其他线程移走,所以此程序不能继续向下运行,运行结果如图:

新建测试用例

package org.example.Collection;

import java.util.concurrent.SynchronousQueue;

public class Sychronous_Queue {
    static class MyService {
        public static SynchronousQueue queue = new SynchronousQueue();

        public void putMethod() {
            try {
                String putString = "anyString" + Math.random();
                queue.put(putString);
                System.out.println(" put=" + putString);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public void takeMethod() {
            try {
                System.out.println("    take=" + queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
        Thread threadPut = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    service.putMethod();
                }
            }
        };
        Thread threadTake = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    service.takeMethod();
                }
            }
        };
        threadTake.start();
        Thread.sleep(2000);
        threadPut.start();
    }
}

运行结果如图:

交替放入与取出。 


总结

Java 的并发集合框架在多线程应用程序中具有重要的意义,它们提供了线程安全的数据结构,可以简化多线程编程并提高性能。通过选择合适的并发集合类,可以有效地处理并发访问和保证数据一致性。这样,开发人员就可以更好地利用多核处理器和并行计算能力,实现高效且安全的并发编程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/221754.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

加密算法有哪几种类型?

加密算法是用于将原始信息转换为不可读形式的算法&#xff0c;以保护数据的安全性和隐私性。根据加密算法的不同&#xff0c;可以将它们分为以下几种类型&#xff1a; 对称加密算法&#xff1a;这种类型的算法使用相同的密钥进行加密和解密。也就是说&#xff0c;发送方和接收方…

装修风格及要求

水电改造 报价&#xff1f; 电线 3C认证国标BV线&#xff08;非BVR&#xff09;&#xff0c;电线上有厂名&#xff0c;买足百米的 厨卫空调4平方线普通插座2.5平方线冰箱2.5平方线照明2.5平方线入户主线6平方或10平方 地面电线点对点&#xff0c;线和线管连接处要有锁扣 品…

STM32(DMA、DHT11)

1、DMA&#xff08;数据的搬运工&#xff09; DMA&#xff0c;全称为&#xff1a;Direct Memory Access&#xff0c;即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输&#xff0c;也没有中断处理方式那样保留现场和恢复现场的过程&#xff0c;通过硬件为 RAM 与 I/O 设备开…

STM32开发基础知识之位操作、宏定义、ifdef条件编译、extern变量申明、typedef类型别名、结构体

一、引言 本文将对STM32入门开发的基本C语言基础知识进行回顾和总结&#xff0c;一边学者在开发过程中能较顺利地进行。主要包括位操作、define宏定义、ifdef条件编译、extern变量申明、typedef类型别名、结构体等基本知识。 二、基础C语言开发知识总结 &#xff08;一&…

25、pytest的测试报告插件allure

allure简介 在这里&#xff0c;你将找到使用allure创建、定制和理解测试报告所需的一切。开始让你的测试沟通更清晰&#xff0c;更有影响力。 Allure Report是一个实用程序&#xff0c;它处理由兼容的测试框架收集的测试结果并生成HTML报告。 安装allure 1、确保安装了Java…

java实验:数据库应用(idea+mysql+php)设计用户注册和登录

设计用户注册和登录界面&#xff0c;实现用户注册和登录操作。 设计用户注册/登录界面;使用工具在MySQL中创建user表&#xff0c;包括学号、姓名、密码、专业、班级&#xff1b;实现注册操作&#xff1a;在user表中插入一条新纪录&#xff0c;但学号不能重复&#xff1b;实现登…

Python爬虫技术:如何利用ip地址爬取动态网页

目录 一、引言 二、Python爬虫基础 三、动态网页结构分析 四、利用ip地址爬取动态网页 1、找到需要爬取的动态网页的URL结构 2、构造请求参数 3、发送请求并获取响应 4、解析响应内容 五、实例代码 六、注意事项 七、总结 一、引言 随着互联网的快速发展&#xff0…

python爬虫混肴DES案例:某影视大数据平台

声明&#xff1a; 该文章为学习使用&#xff0c;严禁用于商业用途和非法用途&#xff0c;违者后果自负&#xff0c;由此产生的一切后果均与作者无关 一、找出需要加密的参数 js运行atob(‘aHR0cHM6Ly93d3cuZW5kYXRhLmNvbS5jbi9Cb3hPZmZpY2UvQk8vTW9udGgvb25lTW9udGguaHRtbA’…

Mysql分布式集群部署---MySQL集群Cluster将数据分成多个片段,每个片段存储在不同的服务器上

1.1 目的 部署MysqlCluster集群环境 1.2 MySQL集群Cluster原理 1 数据分片 MySQL集群Cluster将数据分成多个片段&#xff0c;每个片段存储在不同的服务器上。这样可以将数据负载分散到多个服务器上&#xff0c;提高系统的性能和可扩展性。 2. 数据同步 MySQL集群Cluster使…

Proteus的网络标号与总线

Proteus为了减少过多、复杂的连线&#xff0c;可以使用网络标号与总线配合使用。 Proteus的导线上添加了网络标号&#xff0c;意味着在Proteus上相同的网络标号是连在一起的&#xff0c;所说在图纸上看不出来。 如下图是比较好的Proteus中使用总线的绘制的图纸。可以效仿着画…

【Linux】mkdir 命令使用

mkdir命令 mkdir&#xff08;英文全拼&#xff1a;make directory&#xff09;命令用于创建目录。 著者 作者&#xff1a;David MacKenzie。 mkdir命令 -Linux手册页 语法 mkdir [参数] [文件名] 命令选项及作用 执行令 &#xff1a; mkdir --help 执行命令结果 参数 …

HttpRunner4 Python版(十二)自动化测试平台 实战开发接入案例 技术实现 功能逻辑大致梳理 实行方案初稿

前言 通过之前的文档相信你对HttpRunner 4.x Python版本以后有较为深入的理解和认识了,本文主要讲解 动化测试平台 实战开发接入案例 技术实现 功能逻辑大致梳理 实行方案初稿,后续具体案例需要根据自身项目组的功能去具体实现,并在日常维护工作中逐步完善并增加其健壮性。 …

Leetcode刷题详解——单词拆分

1. 题目链接&#xff1a;139. 单词拆分 2. 题目描述&#xff1a; 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。 **注意&#xff1a;**不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。…

C#,数值计算——计算实对称矩阵所有特征值与特征向量的三角分解与QL迭代法源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// Computes all eigenvalues and eigenvectors of a real symmetric matrix by /// reduction to tridiagonal form followed by QL iteration. /// </summary> pu…

Qlik 成为网络犯罪的焦点

研究人员警告说&#xff0c;Cactus 勒索软件组织正在利用 Qlik Sense 数据可视化、探索和监控解决方案中的关键漏洞来获得对企业网络的初始访问权限。 今年八月下旬&#xff0c;Qlik Sense 开发人员 针对影响 Windows 版本平台的两个关键漏洞发布了补丁 。 其中一个漏洞 CVE-…

【高效开发工具系列】云服务器+Nginx自定义图床

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

Oracle-数据库连接数异常上涨问题分析

问题&#xff1a; 用户的数据库在某个时间段出现连接数异常上涨问题&#xff0c;时间持续5分钟左右&#xff0c;并且问题期间应用无法正常连接请求数据库 从连接数的监控上可以看到数据库平常峰值不到100个连接&#xff0c;在问题时间段突然上涨到400以上 问题分析&#xff1a;…

6页手写笔记总结信号与系统常考知识大题知识点

题型一 判断系统特性题型二 求系统卷积题型三 求三大变换正反变换题型四 求全响应题型五 已知微分方程求系统传递函数题型六 已知系统的传递函数求微分方程题型七 画出系统的零极点图&#xff0c;并判断系统的因果性和稳定性 &#xff08;笔记适合快速复习&#xff0c;可能会有…

Spring-AOP

目录 一、引入AOP 二、核心AOP概念和术语 三、切点表达式 四、Spring实现AOP &#xff08;一&#xff09;AspectJ的支持 1 、基于注解开发 1.1 引入依赖 1.2 实现目标类 1.3 定义切面类&#xff08;日志管理&#xff09; 1.4 将目标类和切面类纳入Spring容器 1.5 开…

分布式搜索引擎(Elastic Search)+消息队列(RabbitMQ)部署

一、分布式搜索引擎&#xff1a;Elastic Search Elastic Search的目标就是实现搜索。是一款非常强大的开源搜索引擎&#xff0c;可以帮助我们从海量数据中快速找到需要的内容。在数据量少的时候&#xff0c;我们可以通过索引去搜索关系型数据库中的数据&#xff0c;但是如果数…