并发集合框架

news2024/11/23 22:41:30

目录

前言

正文

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

graphics.h安装后依旧报错

问题解决一&#xff1a; 我在网上找了很多&#xff0c;都说找到graphics.h这个文件&#xff0c;放到include这个目录下&#xff0c;我照做了&#xff0c;然后 当我进行编译时&#xff0c;自动跳到graphics.h这个文件并出现一堆报错 问题解决二&#xff1a; 看一下这两个文件是…

摄像头选型号指南

镜头选型工具 - HiTools - 海康威视 Hikvisionhttps://www.hikvision.com/cn/support/tools/hitools/cl8a9de13648c56d7f/ 1. 海康威视摄像头选型号指南 海康威视摄像头选型号指南-CSDN博客文章浏览阅读3.7k次。跟客服对话比跟AI对话好不了多少&#xff0c;能噎死人&#xff0…

题目:区间更新(蓝桥OJ 3291)

题目描述&#xff1a; 解题思路&#xff1a; 差分模板题。 题解&#xff1a; #include<bits/stdc.h> using namespace std;const int N 1e5 10; int a[N], diff[N] ;//数组的大小不可能开到大于1e9int res(int n, int m) {for(int i 1; i < n; i)cin >&g…

对比两阶段提交,三阶段协议有哪些改进?

本文我们来讨论两阶段提交和三阶段提交协议的过程以及应用。 在分布式系统中&#xff0c;各个节点之间在物理上相互独立&#xff0c;通过网络进行沟通和协调。在关系型数据库中&#xff0c;由于存在事务机制&#xff0c;可以保证每个独立节点上的数据操作满足 ACID。但是&…

AI模型部署 | onnxruntime部署YOLOv8分割模型详细教程

本文首发于公众号【DeepDriving】&#xff0c;欢迎关注。 0. 引言 我之前写的文章《基于YOLOv8分割模型实现垃圾识别》介绍了如何使用YOLOv8分割模型来实现垃圾识别&#xff0c;主要是介绍如何用自定义的数据集来训练YOLOv8分割模型。那么训练好的模型该如何部署呢&#xff1f…

小航助学题库白名单竞赛考级蓝桥杯等考scratch(10级)(含题库教师学生账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09; 需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;

chineseocr项目不使用web推理-docker容器化

整个流程介绍 拉取 ufoym/deepo 镜像 -- 因为包含了主流深度学习框架&#xff0c;镜像4G出头。拉取 chineseocr 项目代码。修改代码&#xff0c;不使用web&#xff0c;增加命令行传入图片路径的功能打包成docker镜像。 开始 拉取 ufoym/deepo 镜像 &#xff1a;cpu版本为例 do…

封装ThreadLocal

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 为什么要封装ThreadLoc…

geemap学习笔记020:如何搜索Earth Engine Python脚本

前言 本节内容比较简单&#xff0c;但是对于自主学习比较重要&#xff0c;JavaScript提供了很多的示例代码&#xff0c;为了便于学习&#xff0c;geemap将其转为了Python代码。 Earth Engine Python脚本 import ee import geemapee.Initialize()geemap.ee_search() #搜索Ear…

Vue3网站用户引导功能【Intro.js】

一、介绍 Intro.js 是一个用于创建网站用户引导、功能介绍和教程的 JavaScript 库。它允许开发者通过步骤和提示突出显示网站上的特定元素&#xff0c;以帮助用户更好地了解和使用网站的功能。以下是 Intro.js 的一些关键特点和用法介绍&#xff1a; 更多Intro.js 功能网址&a…

图扑数字孪生压缩空气储能管控平台

压缩空气储能在解决可再生能源不稳定性和提供可靠能源供应方面具有重要的优势。压缩空气储能&#xff0c;是指在电网负荷低谷期将电能用于压缩空气&#xff0c;在电网负荷高峰期释放压缩空气推动汽轮机发电的储能方式。通过提高能量转换效率、增加储能密度、快速启动和调节能力…

电子编曲软件FL Studio2024汉化中文免费版下载

电子编曲需要什么软件&#xff1f;市面上的宿主软件都可以完成电子编曲的工作&#xff0c;主要适用电子音乐风格编曲的宿主软件有FL Studio、Ableton Live等。电子编曲需要什么基础&#xff1f;需要对于电子音乐足够熟悉、掌握基础乐理知识以及宿主软件的使用方法。 就我个人的…

Linux cgroup技术

cgroup 全称是 control group&#xff0c;顾名思义&#xff0c;它是用来做“控制”的。控制什么东西呢&#xff1f;当然是资源的使用了。 cgroup 定义了下面的一系列子系统&#xff0c;每个子系统用于控制某一类资源。 CPU 子系统&#xff0c;主要限制进程的 CPU 使用率。cpu…

王道数据结构课后代码题p175 06.已知一棵树的层次序列及每个结点的度,编写算法构造此树的孩子-兄弟链表。(c语言代码实现)

/* 此树为 A B C D E F G 孩子-兄弟链表为 A B E C F G D */ 本题代码如下 void createtree(tree* t, char a[], int degree[], int n) {// 为B数组分配内存tree* B (tree*)malloc(sizeof(tree) * n);int i 0;i…

CENTOS 7 添加黑名单禁止IP访问服务器

一、通过 firewall 添加单个黑名单 只需要把ip添加到 /etc/hosts.deny 文件即可&#xff0c;格式 sshd:$IP:deny vim /etc/hosts.deny# 禁止访问sshd:*.*.*.*:deny# 允许的访问sshd:.*.*.*:allowsshd:.*.*.*:allow 二、多次失败登录即封掉IP&#xff0c;防止暴力破解的脚本…

搞程序权益系统v1.1

继1.0出来后我就把antdui换成elem 新增号卡功能现在只支持对接号氪系统 大家问我这个程序到底有什么用&#xff0c;我这边已经在写和WordPress对接文件&#xff0c;到时候在WordPress网站打开该程序就可以把订单同步到你的程序里面去&#xff0c;当然自己有集成能力也可以到小…

FairGuard无缝兼容小米澎湃OS、ColorOS 14 、鸿蒙4!

随着移动互联网时代的发展&#xff0c;各大手机厂商为打造生态系统、构建自身的技术壁垒&#xff0c;纷纷投身自研操作系统。 而对于一款游戏安全产品&#xff0c;在不同操作系统下&#xff0c;是否能够无缝兼容并且提供稳定的、高强度的加密保护&#xff0c;成了行业的一大痛…

python笔记:dtaidistance

1 介绍 用于DTW的库纯Python实现和更快的C语言实现 2 DTW举例 2.1 绘制warping 路径 from dtaidistance import dtw from dtaidistance import dtw_visualisation as dtwvis import numpy as np import matplotlib.pyplot as plts1 np.array([0., 0, 1, 2, 1, 0, 1, 0, 0…

Redis 命令全解析之 String类型

文章目录 ⛄String 介绍⛄命令⛄对应 RedisTemplate API⛄应用场景 ⛄String 介绍 String 类型&#xff0c;也就是字符串类型&#xff0c;是Redis中最简单的存储类型。 其value是字符串&#xff0c;不过根据字符串的格式不同&#xff0c;又可以分为3类&#xff1a; ● string&…

javaScript(四):函数和常用对象

文章目录 1、函数介绍2、函数的作用3、函数语法4、常用对象&#xff1a;数组5、常用对象&#xff1a;String6、常用对象&#xff1a;自定义对象 1、函数介绍 函数是一段可重复使用的代码块&#xff0c;用于执行特定任务或计算并返回结果。 函数由以下几个要素组成&#xff1a; …