多线程---并发容器的使用

并发容器的原理,7大并发容器详解、及使用场景 并发容器的原理,7大并发容器详解、及使用场景 http://youzhixueyuan.com/use-of-concurrent-containers.html 并发容器的由来 在Java并发编程中,经常听到Java集合类,同步容器、并发容器,那么他们有哪些具体分类,以及各自之间的区别和优劣呢? 只有把这些梳理清楚了,你才能真正掌握在高并发的环境下,正确使用好并发容器,我们先从Java集... 阅读详情

1. 容器概览

在这里插入图片描述

2. 容器的使用

1. Map

多线程环境下,几种Map的处理效率和线程安全问题

测试方案:准备10万条数据, 10个线程, 每个线程处理1万条数据, 并发执行。

数据准备


public class Constants {
    public static final int COUNT = 1000000;
    public static final int THREAD_COUNT = 100;
}

1. HashTable

HashTable是线程安全的, 因为里面加了很多synchronized, 每次put操作, 都会锁整个Map

public class T01_TestHashtable {

    //HashMap是jdk1.0产生的, 最早出现, 里面的很多方法都加了synchronized锁, 所以效率比较低
    static Hashtable<UUID, UUID> m = new Hashtable<>();

    static int count = Constants.COUNT;
    static UUID[] keys = new UUID[count];
    static UUID[] values = new UUID[count];
    static final int THREAD_COUNT = Constants.THREAD_COUNT;

    //先准备好测试用例, 减少干扰因素
    static {
        for (int i = 0; i < count; i++) {
            keys[i] = UUID.randomUUID();
            values[i] = UUID.randomUUID();
        }
    }

    /**
     * 每个线程处理1万条数据,
     */
    static class MyThread extends Thread {
        int start;
        int gap = count / THREAD_COUNT;

        public MyThread(int start) {
            this.start = start;
        }

        @Override
        public void run() {
            //从start开始, 到start+gap个结束
            for (int i = start; i < start + gap; i++) {
                m.put(keys[i], values[i]);
            }
        }
    }

    public static void main(String[] args) {

        long start = System.currentTimeMillis();

        Thread[] threads = new Thread[THREAD_COUNT];

        for (int i = 0; i < threads.length; i++) {
            //设置每个线程的起始值不一样
            threads[i] =
                    new MyThread(i * (count / THREAD_COUNT));
        }

        for (Thread t : threads) {
            t.start();
        }

        for (Thread t : threads) {
            try {
                //主线程等待每个线程都执行结束
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        long end = System.currentTimeMillis();
        //插入执行的时间
        System.out.println(end - start);

        System.out.println(m.size());

        //-----------------------------------

        start = System.currentTimeMillis();
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 10000000; j++) {
                    m.get(keys[10]);
                }
            });
        }

        for (Thread t : threads) {
            t.start();
        }

        for (Thread t : threads) {
            try {
                //主线程需要等待所有子线程执行完
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}
  • 执行结果

写耗时: 290
1000000
读耗时: 29602

可以看到执行时间在300毫秒左右

2. HashMap

sun在HashTablede的基础上推出了无锁的HashMap

public class T02_TestHashMap {

    //sun在HashTablede的基础上推出了无锁的HashMap
    static HashMap<UUID, UUID> m = new HashMap<>();

    static int count = Constants.COUNT;
    static UUID[] keys = new UUID[count];
    static UUID[] values = new UUID[count];
    static final int THREAD_COUNT = Constants.THREAD_COUNT;

    static {
        for (int i = 0; i < count; i++) {
            keys[i] = UUID.randomUUID();
            values[i] = UUID.randomUUID();
        }
    }

    static class MyThread extends Thread {
        int start;
        int gap = count / THREAD_COUNT;

        public MyThread(int start) {
            this.start = start;
        }

        @Override
        public void run() {
            for (int i = start; i < start + gap; i++) {
                //HashMap在resize(扩容)时, 可能会出现多个线程同时进行, 导致数据丢失甚至死锁情况
                m.put(keys[i], values[i]);
            }
        }
    }

    public static void main(String[] args) {

        long start = System.currentTimeMillis();

        Thread[] threads = new Thread[THREAD_COUNT];

        for (int i = 0; i < threads.length; i++) {
            threads[i] =
                    new MyThread(i * (count / THREAD_COUNT));
        }

        for (Thread t : threads) {
            t.start();
        }

        for (Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        long end = System.currentTimeMillis();
        System.out.println(end - start);

        System.out.println(m.size());
    }
}
  • 执行结果

写耗时: 171
814683
读耗时: 2005

可以看到即使执行成功, 总数也不是10万条, 因为设计到Map的resize扩容, 多线程环境下, 同时进入扩容操作, 会出现问题, 具体参考: https://www.jianshu.com/p/e2f75c8cce01

3. SynchronizedHashMap

相当于有锁版的HashMap, 锁的粒度比HashTable小了一些


public class T03_TestSynchronizedHashMap {

    //相当于有锁版的HashMap, 锁的粒度比HashTable小了一些
    static Map<UUID, UUID> m = Collections.synchronizedMap(new HashMap<UUID, UUID>());

    static int count = Constants.COUNT;
    static UUID[] keys = new UUID[count];
    static UUID[] values = new UUID[count];
    static final int THREAD_COUNT = Constants.THREAD_COUNT;

    static {
        for (int i = 0; i < count; i++) {
            keys[i] = UUID.randomUUID();
            values[i] = UUID.randomUUID();
        }
    }

    static class MyThread extends Thread {
        int start;
        int gap = count / THREAD_COUNT;

        public MyThread(int start) {
            this.start = start;
        }

        @Override
        public void run() {
            for (int i = start; i < start + gap; i++) {
                m.put(keys[i], values[i]);
            }
        }
    }

    public static void main(String[] args) {

        long start = System.currentTimeMillis();

        Thread[] threads = new Thread[THREAD_COUNT];

        for (int i = 0; i < threads.length; i++) {
            threads[i] =
                    new MyThread(i * (count / THREAD_COUNT));
        }

        for (Thread t : threads) {
            t.start();
        }

        for (Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        long end = System.currentTimeMillis();
        System.out.println(end - start);

        System.out.println(m.size());

        //-----------------------------------

        start = System.currentTimeMillis();
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 10000000; j++) {
                    m.get(keys[10]);
                }
            });
        }

        for (Thread t : threads) {
            t.start();
        }

        for (Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}
  • 执行结果

写耗时: 334
1000000
读耗时: 29575

4. ConcurrentHashMap

ConcurrentHashMap并发插入的时候, 效率未必比其他Map高, 但是并发读取的时候效率很

/**
 * ConcurrentHashMap并发插入的时候, 效率未必比其他Map高, 但是并发读取的时候效率很高
 */
public class T04_TestConcurrentHashMap {

    //结合了JUC包里的一些锁
    static Map<UUID, UUID> m = new ConcurrentHashMap<>();

    static int count = Constants.COUNT;
    static UUID[] keys = new UUID[count];
    static UUID[] values = new UUID[count];
    static final int THREAD_COUNT = Constants.THREAD_COUNT;

    static {
        for (int i = 0; i < count; i++) {
            keys[i] = UUID.randomUUID();
            values[i] = UUID.randomUUID();
        }
    }

    static class MyThread extends Thread {
        int start;
        int gap = count/THREAD_COUNT;

        public MyThread(int start) {
            this.start = start;
        }

        @Override
        public void run() {
            for(int i=start; i<start+gap; i++) {
                m.put(keys[i], values[i]);
            }
        }
    }

    public static void main(String[] args) {

        long start = System.currentTimeMillis();

        Thread[] threads = new Thread[THREAD_COUNT];

        for(int i=0; i<threads.length; i++) {
            threads[i] =
            new MyThread(i * (count/THREAD_COUNT));
        }

        for(Thread t : threads) {
            t.start();
        }

        for(Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        long end = System.currentTimeMillis();
        System.out.println("写耗时: " + (end - start));

        System.out.println(m.size());

        //-----------------------------------

        start = System.currentTimeMillis();
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(()->{
                for (int j = 0; j < 10000000; j++) {
                    m.get(keys[10]);
                }
            });
        }

        for(Thread t : threads) {
            t.start();
        }

        for(Thread t : threads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        end = System.currentTimeMillis();
        System.out.println("读耗时: " + (end - start));
    }
}

写耗时: 160
1000000
读耗时: 1108

2. Collection

多线程环境下集合的使用, 由一个测试题引入

测试题:

有N张火车票,每张票都有一个编号
同时有10个窗口对外售票
请写一个模拟程序

1. ArrayList

/**
 * 有N张火车票,每张票都有一个编号
 * 同时有10个窗口对外售票
 * 请写一个模拟程序
 *
 * 分析下面的程序可能会产生哪些问题?
 * 重复销售?超量销售?
 *
 *
 * @author cyc
 */
package com.cyc.juc.c_024_FromVectorToQueue;

import java.util.ArrayList;
import java.util.List;

public class TicketSeller1ArrayList {
    static List<String> tickets = new ArrayList<>();

    static {
        for (int i = 0; i < 10000; i++) {
            //提前将票添加到集合中
            tickets.add("票编号:" + i);
        }
    }


    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (tickets.size() > 0) {
                    //有可能只剩了一张票, 但是多个线程都通过了这个判断, 然后多线程直接开始remove, 所以会导致超卖的情况
                    System.out.println("销售了--" + tickets.remove(0));
                }
            }).start();
        }
    }
}

执行结果

Exception in thread “Thread-7” java.lang.ArrayIndexOutOfBoundsException: -1
at java.util.ArrayList.remove(ArrayList.java:505)
at com.cyc.juc.c_024_FromVectorToQueue.TicketSeller1ArrayList.lambda$main$0(TicketSeller1ArrayList.java:32)
at java.lang.Thread.run(Thread.java:748)

最后多个线程都进入了判断, 并开始卖最后一张票, 导致数组越界异常。

Queue和List对比

  • Queue中提供了一些对线程友好的API offer, peek, poll

BlockingQueue

中的put方法和take方法可实现线程堵塞

2. Vector

/**
 * 有N张火车票,每张票都有一个编号
 * 同时有10个窗口对外售票
 * 请写一个模拟程序
 *
 * 分析下面的程序可能会产生哪些问题?
 *
 * 使用Vector或者Collections.synchronizedXXX
 * 分析一下,这样能解决问题吗?
 *
 * @author cyc
 */
package com.cyc.juc.c_024_FromVectorToQueue;

import java.util.Vector;
import java.util.concurrent.TimeUnit;

public class TicketSeller2Vector {
    static Vector<String> tickets = new Vector<>();


    static {
        for (int i = 0; i < 1000; i++) {
            tickets.add("票 编号:" + i);
        }
    }

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (tickets.size() > 0) {
                    System.out.println("销售了--" + tickets.remove(0));
                }
            }).start();
        }
    }
}
  • 执行结果可以正常输出

但是如果在

while (tickets.size() > 0) {
                    System.out.println("销售了--" + tickets.remove(0));
                }

之中还有其他业务逻辑需要执行, 假设执行10毫秒, 即代码如下

while (tickets.size() > 0) {
    //所谓的线程安全 , 是调用size()和remove()方法时加锁了, 但是在执行完size()之前和remove()之后, 并没有加锁,
    //这个过程就不是原子的
    try {
        TimeUnit.MILLISECONDS.sleep(10);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }


    System.out.println("销售了--" + tickets.remove(0));
}
  • 输出结果
Exception in thread "Thread-9" java.lang.ArrayIndexOutOfBoundsException: Array index out of range: 0
	at java.util.Vector.remove(Vector.java:834)
	at com.cyc.juc.c_024_FromVectorToQueue.TicketSeller2Vector.lambda$main$0(TicketSeller2Vector.java:42)
	at java.lang.Thread.run(Thread.java:748)

同样导致数据越界异常, 因为Vector的size和remove操作执行过程中不是原子的

  • 优化

给tickets加锁, 并在代码中判断tickets.size()是否等于0


package com.cyc.juc.c_024_FromVectorToQueue;

import java.util.Vector;
import java.util.concurrent.TimeUnit;

public class TicketSeller2Vector {
    static Vector<String> tickets = new Vector<>();


    static {
        for (int i = 0; i < 1000; i++) {
            tickets.add("票 编号:" + i);
        }
    }

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (tickets.size() > 0) {
                    synchronized (tickets) {
                        //所谓的线程安全 , 是调用size()和remove()方法时加锁了, 但是在执行完size()之前和remove()之后, 并没有加锁,
                        //这个过程就不是原子的

                        if (tickets.size() <= 0) {
                            break;
                        }

                        try {
                            TimeUnit.MILLISECONDS.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }


                        System.out.println("销售了--" + tickets.remove(0));
                    }
                }
            }).start();
        }
    }
}

3. LinkedList

/**
 * 有N张火车票,每张票都有一个编号
 * 同时有10个窗口对外售票
 * 请写一个模拟程序
 *
 * 分析下面的程序可能会产生哪些问题?
 * 重复销售?超量销售?
 *
 * 使用Vector或者Collections.synchronizedXXX
 * 分析一下,这样能解决问题吗?
 *
 * 就算操作A和B都是同步的,但A和B组成的复合操作也未必是同步的,仍然需要自己进行同步
 * 就像这个程序,判断size和进行remove必须是一整个的原子操作
 *
 * @author cyc
 */
package com.cyc.juc.c_024_FromVectorToQueue;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class TicketSeller3VoctorWithSynchronize {
    static List<String> tickets = new LinkedList<>();


    static {
        for (int i = 0; i < 1000; i++) {
            tickets.add("票 编号:" + i);
        }
    }

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    //给tickets对象加锁, 保证原子性, 实现线程安全
                    synchronized (tickets) {
                        if (tickets.size() <= 0) {
                            break;
                        }

                        try {
                            TimeUnit.MILLISECONDS.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                        System.out.println("销售了--" + tickets.remove(0));
                    }
                }
            }).start();
        }
    }
}

4. ConcurrentLinkedQueue

/**
 * 有N张火车票,每张票都有一个编号
 * 同时有10个窗口对外售票
 * 请写一个模拟程序
 *
 * 分析下面的程序可能会产生哪些问题?
 * 重复销售?超量销售?
 *
 * 使用Vector或者Collections.synchronizedXXX
 * 分析一下,这样能解决问题吗?
 *
 * 就算操作A和B都是同步的,但A和B组成的复合操作也未必是同步的,仍然需要自己进行同步
 * 就像这个程序,判断size和进行remove必须是一整个的原子操作
 *
 * 使用ConcurrentQueue提高并发性
 *
 * @author cyc
 */
public class TicketSeller4ConcurrentLinkedQueue {
    //多线程的程序应多考虑使用队列
    static Queue<String> tickets = new ConcurrentLinkedQueue<>();


    static {
        for (int i = 0; i < 1000; i++) {
            tickets.add("票 编号:" + i);
        }
    }

    public static void main(String[] args) {

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    String s = tickets.poll();
                    if (s == null) {
                        break;
                    } else {
                        System.out.println("销售了--" + s);
                    }
                }
            }).start();
        }
    }
}

执行结果正常, 并且速度也很快

3. 并发容器的使用

1. ConcurrentHashMap

/**
 * 几种并发容器Map的使用
 * ConcurrentSkipListMap参考 https://blog.youkuaiyun.com/sunxianghuang/article/details/52221913
 */
public class T01_ConcurrentMap {
    public static void main(String[] args) {
        Map<String, String> map = new ConcurrentHashMap<>();//高并发无序
//    Map<String, String> map = new ConcurrentSkipListMap<>(); //高并发并且排序
        

        //Map<String, String> map = new Hashtable<>();
        //Map<String, String> map = new HashMap<>(); //Collections.synchronizedXXX
        //TreeMap
        Random r = new Random();
        Thread[] threads = new Thread[100];
        //倒数门栓
        CountDownLatch latch = new CountDownLatch(threads.length);
        long start = System.currentTimeMillis();
        //100个线程并发往map里各写入10000条数据
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    map.put("a" + r.nextInt(100000), "a" + r.nextInt(100000));
//             map.put("a" + m.getAndIncrement(), "a" + m.getAndIncrement());
                }
                latch.countDown();
            });
        }

        Arrays.asList(threads).forEach(t -> t.start());
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long end = System.currentTimeMillis();
        System.out.println(end - start);
        System.out.println(map.size());

    }
}

2. CopyOnWriteArrayList

/**
 * 写时复制容器 copy on write
 * 多线程环境下,写时效率低,读时效率高
 * 适合写少读多的环境
 * 因为每次写的时候都要复制一份,
 * 
 * @author cyc
 */
package com.cyc.juc.c_025;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

public class T02_CopyOnWriteList {
   public static void main(String[] args) {
      List<String> lists = 
//          new ArrayList<>(); //这个会出并发问题!
//          new Vector();
            new CopyOnWriteArrayList<>();//写的时候加锁, 读的时候不加锁
      Random r = new Random();
      Thread[] threads = new Thread[100];
      
      for(int i=0; i<threads.length; i++) {
         Runnable task = new Runnable() {
   
            @Override
            public void run() {
               for(int i=0; i<1000; i++) {
                  lists.add("a" + r.nextInt(10000));
               }
            }
            
         };
         threads[i] = new Thread(task);
      }
      
      //写操作
      runAndWriteComputeTime(threads);

      for(int i=0; i<threads.length; i++) {
         Runnable task = () -> {
            for (String list : lists) {
               String s = list;
            }
         };
         threads[i] = new Thread(task);
      }
      //读操作
      runAndReadComputeTime(threads);

      
      System.out.println("list大小: "+lists.size());
   }

   private static void runAndReadComputeTime(Thread[] threads) {
      long s1 = System.currentTimeMillis();
      Arrays.asList(threads).forEach(t->t.start());
      Arrays.asList(threads).forEach(t->{
         try {
            //主线程等待所有线程执行完
            t.join();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      });
      long s2 = System.currentTimeMillis();
      System.out.println("读耗时: "+(s2 - s1));
   }

   static void runAndWriteComputeTime(Thread[] threads) {
      long s1 = System.currentTimeMillis();
      Arrays.asList(threads).forEach(t->t.start());
      Arrays.asList(threads).forEach(t->{
         try {
            //主线程等待所有线程执行完
            t.join();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      });
      long s2 = System.currentTimeMillis();
      System.out.println("写耗时: "+(s2 - s1));
      
   }
}
  • 先来测试同样为线程安全的collection容器Vector

输出结果

写耗时: 86
读耗时: 401
list大小: 100000

  • 测试CopyOnWriteArrayList

输出结果

写耗时: 2769
读耗时: 41
list大小: 100000

可以看到CopyOnWriteArrayList写操作比较慢, 这正是由于每次写入都要进行复制操作 , 但是读数据非常快。

3. ConcurrentLinkedQueue

package com.cyc.juc.c_025;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * ConcurrentLinkedQueue并发队列, 先进先出
 */
public class T04_ConcurrentQueue {
   public static void main(String[] args) {
      Queue<String> strs = new ConcurrentLinkedQueue<>();
      
      for(int i=0; i<10; i++) {
         strs.offer("a" + i);  //add
      }
      
      System.out.println(strs);
      
      System.out.println(strs.size());
      //poll取出, 并删除一个
      System.out.println(strs.poll());
      System.out.println(strs.size());

      //peak 取出 , 不删除
      System.out.println(strs.peek());
      System.out.println(strs.size());

   }
}
  • 输出结果

[a0, a1, a2, a3, a4, a5, a6, a7, a8, a9]
10
a0
9
a1
9

4. LinkedBlockingQueue

  • LinkedBlockingQueue中的锁是分离的,生产者的锁PutLock,消费者的锁takeLock
    在这里插入图片描述

  • LinkedBlockingQueue内部维护的是一个链表结构
    在这里插入图片描述

  • 在生产和消费的时候,需要创建Node对象进行插入或移除,大批量数据的系统中,其对于GC的压力会比较大

    在这里插入图片描述

  • LinkedBlockingQueue有默认的容量大小为:Integer.MAX_VALUE,当然也可以传入指定的容量大小

代码演示

/**
 * BlockingQueue天生的阻塞队列
 */
public class T05_LinkedBlockingQueue {

   //
   static BlockingQueue<String> strs = new LinkedBlockingQueue<>();

   static Random r = new Random();

   public static void main(String[] args) {
      new Thread(() -> {
         for (int i = 0; i < 100; i++) {
            try {
               strs.put("a" + i); //如果满了,就会等待
               TimeUnit.MILLISECONDS.sleep(r.nextInt(1000));
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }, "p1").start();

      for (int i = 0; i < 5; i++) {
         new Thread(() -> {
            for (;;) {
               try {
                  System.out.println(Thread.currentThread().getName() + " take -" + strs.take()); //如果空了,就会等待
               } catch (InterruptedException e) {
                  e.printStackTrace();
               }
            }
         }, "c" + i).start();

      }
   }
}

5. ArrayBlockingQueue

  • ArrayBlockingQueue生产者和消费者使用的是同一把锁
    在这里插入图片描述

  • ArrayBlockingQueue内部维护了一个数组

    在生产和消费的时候,是直接将枚举对象插入或移除的,不会产生或销毁任何额外的对象实例

    在这里插入图片描述

    插入操作

    在这里插入图片描述

  • ArrayBlockingQueue在初始化的时候,必须传入一个容量大小的值

在这里插入图片描述

6. DelayQueue

package com.cyc.juc.c_025;

import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * 按照等待时间长短排序执行 , 可以自定义执行优先级
 * 用户: 按照时间进行调度,
 */
public class T07_DelayQueue {

    static BlockingQueue<MyTask> tasks = new DelayQueue<>();

    static Random r = new Random();

    static class MyTask implements Delayed {
        String name;
        long runningTime;

        MyTask(String name, long rt) {
            this.name = name;
            this.runningTime = rt;
        }

        @Override
        public int compareTo(Delayed o) {
            if (this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS)) {
                return -1;
            } else if (this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS)) {
                return 1;
            } else {
                return 0;
            }
        }

        @Override
        public long getDelay(TimeUnit unit) {

            return unit.convert(runningTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }


        @Override
        public String toString() {
            return name + " " + runningTime;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        long now = System.currentTimeMillis();
        MyTask t1 = new MyTask("t1", now + 1000);
        MyTask t2 = new MyTask("t2", now + 2000);
        MyTask t3 = new MyTask("t3", now + 1500);
        MyTask t4 = new MyTask("t4", now + 2500);
        MyTask t5 = new MyTask("t5", now + 500);

        tasks.put(t1);
        tasks.put(t2);
        tasks.put(t3);
        tasks.put(t4);
        tasks.put(t5);

        System.out.println(tasks);

        for (int i = 0; i < 5; i++) {
            System.out.println(tasks.take());
        }
    }
}

7. SynchronousQueue

/**
 * 容量为0
 * SynchronousQueue 同步Queue
 * 常用作一个线程给另一个容器下单任务
 * @author fei
 */
public class T08_SynchronusQueue { //容量为0
   public static void main(String[] args) throws InterruptedException {
      BlockingQueue<String> strs = new SynchronousQueue<>();
      
      new Thread(()->{
         try {
            System.out.println(strs.take());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }).start();

      strs.put("aaa"); //阻塞等待消费者消费
      //必须有消费者进行消费才可以继续put,如果没有消费者, 则这里就会堵塞在这里
//    strs.put("bbb");
      //strs.add("aaa");
      System.out.println(strs.size());
   }
}

8. LinkedTransferQueue

package com.cyc.juc.c_025;

import java.util.concurrent.LinkedTransferQueue;

/**
 * 给线程传递任务
 * 用于需要得到执行结果, 才能继续往下执行, 类似MQ的一个返回处理结果机制
 * @author cyc
 */
public class T09_TransferQueue {
   public static void main(String[] args) throws InterruptedException {
      LinkedTransferQueue<String> strs = new LinkedTransferQueue<>();
      
      new Thread(() -> {
         try {
            System.out.println(strs.take());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }).start();
      //和put的区别在于, 线程put完之后, 执行put的线程就可以走了,
      // 而transfer, 执行transfer的线程必须原地等着, 阻塞住, 直到有线程取走transfer的值才行
      //put是满了才等着, transfer是来了就得等着
      strs.transfer("aaa");
      
      //strs.put("aaa");


      /*new Thread(() -> {
         try {
            System.out.println(strs.take());
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }).start();*/


   }
}

3. 练习题

要求用线程顺序打印A1B2C3…Z26

1. 使用LockSupport

/**
 * Locksupport park 当前线程阻塞(停止)
 * unpark(Thread t)
 */
public class T02_00_LockSupport {


    static Thread t1 = null, t2 = null;

    public static void main(String[] args) throws Exception {
        char[] aC = "ABCDEFG".toCharArray();
        char[] aI = "1234567".toCharArray();

        t1 = new Thread(() -> {

            for(char c : aC) {
                System.out.print(c);
                LockSupport.unpark(t2); //叫醒t2
                LockSupport.park(); //t1阻塞
            }
        }, "t1");

        t2 = new Thread(() -> {
            for(char c : aI) {
                //保证t1先执行
                LockSupport.park(); //T2阻塞
                System.out.print(c);
                LockSupport.unpark(t1); //叫醒T1
            }
        }, "t2");

        t1.start();
        t2.start();
    }
}

输出结果

A1B2C3D4E5F6G7

2. 使用synchronized, notify,wait

public class T06_00_sync_wait_notify {
    public static void main(String[] args) {
        //新建一个需要被锁的对象
        final Object o = new Object();

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        new Thread(()->{
            synchronized (o) {
                for(char c : aI) {
                    System.out.print(c);
                    try {
                        o.notify();//叫醒第二个线程
                        //这里为什么使用wait而不是sleep, wait可以释放锁, sleep不会释放锁
                        o.wait(); //让出锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //必须,否则无法停止程序, 因为两个线程最后会有一个是wait状态,
                // 如果不进行o.notify(),则无法叫醒那个wait()的线程
                o.notify();
            }

        }, "t1").start();

        new Thread(()->{
            synchronized (o) {
                for(char c : aC) {
                    System.out.print(c);
                    try {
                        o.notify();
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                o.notify();
            }
        }, "t2").start();
    }
}

保证执行顺序 , 先打印A, 再打印1, 然后依次B,2,C,3…

使用CountDownLatch保证顺序

package com.cyc.juc.c_026_00_interview.A1B2C3;


import java.util.concurrent.CountDownLatch;

/**
 * 多线程保证执行顺序, 使A先输出, 再输出1
 */
public class T07_00_sync_wait_notify_sort {

    private static CountDownLatch latch = new CountDownLatch(1);

    public static void main(String[] args) {
        final Object o = new Object();



        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        new Thread(()->{
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o) {

                for(char c : aI) {
                    System.out.print(c);
                    try {
                        //唤醒第二个线程
                        o.notify();
                        //让出锁
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                o.notify();
            }
        }, "t1").start();

        new Thread(()->{

            synchronized (o) {
                for(char c : aC) {
                    System.out.print(c);
                    //输出之后, 进行减1
                    latch.countDown();
                    try {
                        //唤醒第一个线程
                        o.notify();
                        //让出锁
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify();
            }
        }, "t2").start();
    }
}

使用volatile+标识字段

package com.cyc.juc.c_026_00_interview.A1B2C3;


import java.util.concurrent.CountDownLatch;

/**
 * 多线程保证执行顺序, 使A先输出, 再输出1
 */
public class T07_00_sync_wait_notify_sort {

    private static volatile boolean t2Started = false;

    public static void main(String[] args) {
        final Object o = new Object();



        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        new Thread(()->{
            synchronized (o) {

                while(!t2Started) {
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                for(char c : aI) {
                    System.out.print(c);
                    try {
                        //唤醒第二个线程
                        o.notify();
                        //让出锁
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                o.notify();
            }
        }, "t1").start();

        new Thread(()->{

            synchronized (o) {
                for(char c : aC) {
                    System.out.print(c);
                    t2Started = true;
                    try {
                        //唤醒第一个线程
                        o.notify();
                        //让出锁
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify();
            }
        }, "t2").start();
    }
}

3. 使用Lock.newCondition()

/**
 * 使用lock.newCondition()创建等待队列
 * @author cyc
 */
public class T08_00_lock_condition {

    public static void main(String[] args) {

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        new Thread(()->{
            try {
                lock.lock();

                for(char c : aI) {
                    System.out.print(c);
                    //唤醒一个等待在condition上的线程,将该线程从等待队列中转移到同步队列中
                    condition.signal();
                    //当前线程进入等待状态
                    condition.await();
                }

                condition.signal();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }, "t1").start();

        new Thread(()->{
            try {
                lock.lock();

                for(char c : aC) {
                    System.out.print(c);
                    condition.signal();
                    condition.await();
                }

                condition.signal();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }, "t2").start();
    }
}

优化

/**
 * Condition本质是锁资源上不同的等待队列
 */
public class T09_00_lock_condition_two {

    public static void main(String[] args) {

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        Lock lock = new ReentrantLock();
        //condition实际上是一个等待队列
        Condition conditionT1 = lock.newCondition();
        Condition conditionT2 = lock.newCondition();

        new Thread(()->{
            try {
                lock.lock();

                for(char c : aI) {
                    System.out.print(c);
                    conditionT2.signal();
                    conditionT1.await();
                }

                conditionT2.signal();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }, "t1").start();

        new Thread(()->{
            try {
                lock.lock();

                for(char c : aC) {
                    System.out.print(c);
                    conditionT1.signal();
                    conditionT2.await();
                }

                conditionT1.signal();

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }, "t2").start();
    }
}

4. 使用Exchanger

package com.cyc.juc.c_026_00_interview.A1B2C3;

import java.util.concurrent.Exchanger;

public class T12_00_Exchanger_Not_Work {
    private static Exchanger<String> exchanger = new Exchanger<>();

    public static void main(String[] args) {
        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        new Thread(()->{
            for(int i=0; i<aI.length; i++) {
                System.out.print(aI[i]);
                try {
                    //Exchanger类中的exchange(String x) 方法具有阻塞的特点,也就是说此方法被调用后等待其他线程来获取数据,
                    // 如果没有其他线程取得数据,则就会一直阻塞等待下去。
                    exchanger.exchange("T1");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(()->{
            for(int i=0; i<aC.length; i++) {
                try {
                    exchanger.exchange("T2");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.print(aC[i]);
            }
        }).start();
    }
}

5. 使用TransferQueue队列

package com.cyc.juc.c_026_00_interview.A1B2C3;

import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;

/**
 * TransferQueue
 */
public class T13_TransferQueue {
    public static void main(String[] args) {
        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        TransferQueue<Character> queue = new LinkedTransferQueue<>();
        new Thread(()->{
            try {
                for (char c : aI) {
                    //第一个线程作为消费者,去take , 如果没有, 则等待
                    System.out.print(queue.take());
                    queue.transfer(c);
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(()->{
            try {
                for (char c : aC) {
                    //将c交给对方去打印
                    queue.transfer(c);
                    System.out.print(queue.take());
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }
}

6. 使用CAS-1

package com.cyc.juc.c_026_00_interview.A1B2C3;


public class T03_00_cas {

    enum ReadyToRun {T1, T2}

    //保证线程之间的可见性, 当r有改动时, 可以立即通知其他线程进行更新缓存
    static volatile ReadyToRun r = ReadyToRun.T1; 

    public static void main(String[] args) {

        char[] aC = "ABCDEFG".toCharArray();
        char[] aI = "1234567".toCharArray();

        new Thread(() -> {

            for (char c : aC) {
                while (r != ReadyToRun.T1) {}
                System.out.print(c);
                r = ReadyToRun.T2;
            }

        }, "t1").start();

        new Thread(() -> {

            for (char c : aI) {
                while (r != ReadyToRun.T2) {}
                System.out.print(c);
                r = ReadyToRun.T1;
            }
        }, "t2").start();
    }
}

7. 使用CAS-2

package com.cyc.juc.c_026_00_interview.A1B2C3;


import java.util.concurrent.atomic.AtomicInteger;

public class T05_00_AtomicInteger {

    static AtomicInteger threadNo = new AtomicInteger(1);


    public static void main(String[] args) {

        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();


        new Thread(() -> {

            for (char c : aI) {
                while (threadNo.get() != 1) {}
                System.out.print(c);
                threadNo.set(2);
            }

        }, "t1").start();

        new Thread(() -> {

            for (char c : aC) {
                while (threadNo.get() != 2) {}
                System.out.print(c);
                threadNo.set(1);
            }
        }, "t2").start();
    }
}
八路抢答器____一个作品的制作思路 学习了解到一个产品制作完全由一个人去采购,设计,仿真,画PCB。不光是需求的分析还有实现功能时如何搭建电路,和给出一个简单高效的设计方法也是至关重要的。 阅读详情

相关推荐

SPAD 单光子雪崩二极管

SPAD,全称为,是一种高灵敏度的光电探测器件,能够在非常低的光照水平下检测单个光子。SPAD在工作时,当一个光子被探测器吸收后,能够触发一个雪崩效应,产生一个可以被检测到的电信号。

qq_42786033的博客 1万+

Java 多线程(六):容器

SynchronousQueue 也是一个队列,但它的特别之处在于它内部没有容器,一个生产线程,当它生产产品(即put的时候),如果当前没有人想要消费产品(即当前没有线程执行take),此生产线程必须阻塞,等待一个消费线程调用take操作,take操作将会唤醒该生产线程,同时消费线程会获取生产线程的产品(即数据传递)添加元素时会复制一个新的数组,写数据时在新数组上进行,读操作在原数组上进行。允许在写操作时来读取数据,大大提高了读的性能,因此适合读多写少的应用场景,后就被阻塞了,只有消费线程线程进行了消费,

Practitioner 457

数学建模国赛B题保姆级攻略:从选题到代码实现的完整流程(2024最新版)

本文提供2024年数学建模国赛B题的保姆级实战攻略,涵盖从赛题解构、模型构建到算法实现与论文撰写的完整流程。文章详细解析了如何快速抓住问题核心、建立数学模型、进行算法求解与结果可视化,并分享了高效团队协作与专业论文写作的关键技巧,助力参赛者系统性地完成从选题到代码实现的全过程。

weixin_29089057的博客 138

并发编程学习笔记 之 常用并发容器的概念及使用方法

所有线程对该容器的读取操作将不会对数据集合产生加锁的动作,从而使得高并发高吞吐量的读取操作变得高效,但是当有线程对该容器中的数据集合进行删除或增加等写操作时才会对整个数据集合进行加锁操作,然后将容器中的数据集合复制一份,并且基于最新的复制进行删除或增加等写操作,当写操作执行结束以后,将最新复制的数据集合引用指向原有的数据集合,进而达到读写分离最终一致性的目的。并发容器是指在高并发应用程序的使用过程中,这些容器(数据结构)是线程安全的,而且在高并发的程序中运行它们会有高效的性能表现。...

向心行者 655

【02】【VisionMaster V4.0.0 】安装教程—— 授权文件更新报错:应用程序与本地LM通讯出错(怀疑电脑加密 和 上网受限 造成的 )

本文详细介绍了海康VisionMaster 4.0试用版的安装流程及常见问题解决方案。主要内容包括:1)从官网下载基础安装包、试用补丁和授权文件;2)按顺序安装主程序、补丁和授权文件;3)针对授权失败问题提供两种解决方案(更换未加密电脑或使用加密狗);4)说明试用版的功能限制(30天试用期、2个流程限制等)。文章特别指出电脑加密可能导致授权失败,并提供了安装报错时的处理建议,为初次使用者提供了完整的安装指导。

程序员,他们想的是什么?他们想的永远都是技术,他们崇尚的也永远都是技术。 7143

基于YOLOv10的微小目标检测,多头检测器提升小目标检测精度

引入多头检测器助力YOLOv10,暴力提升小目标检测性能

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 2567

多线程(八)并发容器

并发容器

QG 1044

SAP 固定资产采购简介

SAP 固定资产采购是指企业通过采购流程获取长期使用并记录为固定资产的物品。固定资产是企业为长期使用而购置的资产,如机器设备、办公设备、建筑物、车辆等,通常这些资产的使用期限较长,并且在资产负债表上记录。在SAP系统中,固定资产采购流程通常与物料管理(MM)模块和固定资产管理(FI-AA)模块相结合。这个过程涉及从需求的提出、采购订单的创建,到货物接收、发票校验和资产激活的多个步骤。

我小时候很黑的博客 1727

并发容器使用

CopyOnWriteArrayList、ConcurrentSkipListMap、ConcurrentSkipListSet、ConcurrentHashMap、BlockingQuence等并发容器使用:

qq_36955645的博客 214

【知识图谱】Neo4j基本操作及数据库文件导入(graph.db.dump)

初探Neo4j导入数据备份数据客户端操作导入csv文件指定csv文件格式使用命令行导入csv文件附录 请读者参考上篇博文 Neo4j安装教程 导入数据 若开启服务,先在终端停止 Neo4j 服务 ./neo4j stop 接下来,下载射雕三部曲的数据库文件 wget https://labfile.oss.aliyuncs.com/courses/1354/graph.db.dump 下载完将出现 graph.db.dump 文件: 将数据库文件导入到 Neo4j 中 首先进入neo4j 根目录下:

全网粉丝30W+,优快云、稀土掘金人工智能领域签约作者,人工智能、大模型、Python 爬虫,商务合作,请私信沟通。 8615

贝叶斯机器学习在吸附量预测中的应用

本文介绍了贝叶斯机器学习在吸附量预测中的应用。通过Python实现完整的贝叶斯建模流程,包括数据生成、预处理、模型构建与评估。文章对比了贝叶斯与频率学派的差异,详细阐述了贝叶斯线性回归和神经网络的实现方法,并提供了数据可视化、模型诊断等技术细节。该方法不仅能预测吸附量,还能量化预测不确定性,为工程决策提供更全面的依据。全文超过6000字,结合理论推导与代码实践,是应用贝叶斯方法解决实际工程问题的完整指南。

AI的内部世界的博客 165

EASTL 多线程编程终极指南:如何安全地在并发环境中使用容器

EASTL(Electronic Arts Standard Template Library)是专为游戏开发设计的高性能C++模板库,在多线程环境中使用EASTL容器需要特别注意线程安全性问题。本文将为您提供完整的EASTL多线程编程指南,帮助您安全地在并发环境中使用各种容器。🚀 ## 🔑 核心概念:EASTL容器线程安全性 EASTL容器的基本原则是:**单个容器实例不是线程安全的**

gitblog_00231的博客 375

HTML满屏跳动的爱心(完整代码)

HTML语言实现满屏跳动爱心的完整代码。

AIGC 12万+

Java多线程之常用并发容器的使用

文章目录ConcurrentHashMap ConcurrentHashMap 为什么要使用ConcurrentHashMap 在多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%,HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表。 形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就...

supermao1013的专栏 1237

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

多线程三 并发容器简单使用

1.threadLocal 本地线程变量,在每个线程会独立开辟内存空间。 在高并发先不要使用。 private static ThreadLocal local = new ThreadLocal(); public static void main(String[] args) { new Thread(()->{ try { Ti...

用针戳左手中指指头的博客 646

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Java多线程系列(七):并发容器的原理,7大并发容器详解、及使用场景

之前谈过高并发编程系列: 高并发编程系列:4种常用Java线程锁的特点,性能比较、使用场景 高并发编程系列:CountDownLatch、Semaphore等4大并发工具类详解 高并发编程系列:4大JVM性能分析工具详解,及内存泄漏分析方案 高并发编程系列:ConcurrentHashMap的实现原理(JDK1.7和JDK1.8) Java并发编程系列:深入详解Synchronized同步锁的底层实现 今天详细介绍并发容器的实现原理,与同步容器的区别等。 ..

一个有情怀的程序猿博客 433

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

C++ STL并发编程难题破解(多线程环境下容器安全使用指南)

解决多线程下C++ STL容器安全问题,本文提供详尽的C++ STL用法详解,涵盖互斥锁、原子操作等同步机制在vector、map等容器中的实际应用,确保并发环境下的数据一致性与性能优化,值得收藏。

InitPulse的博客 896

Java性能优化(七)-多线程调优-并发容器的使用

跳跃表是基于链表扩展实现的一种特殊链表,类似于树的实现,跳跃表不仅实现了横向链表,还实现了垂直方向的分层索引。一个跳跃表由若干层链表组成,每一层都实现了一个有序链表索引,只有最底层包含了所有数据,每一层由下往上依次通过一个指针指向上层相同值的元素,每层数据依次减少,等到了最顶层就只会保留部分数据了。跳跃表的这种结构,是利用了空间换时间的方法来提高了查询效率。程序总是从最顶层开始查询访问,通过判断元素值来缩小查询范围。我们可以通过以下几张图来了解下跳跃表的具体实现原理。

1779

多线程——并发容器类——Vector(同步容器)HashTable(同步),同步类容器的线程安全使用

1.容器的概述: ConcurrentMap集合类使用与底层原理分析 CopyOnWrite集合类使用与底层原理分析 并发与阻塞队列Queue讲解 模拟阻塞队列实战 ArrayBlockingQueue ConcurrentLinkedQueue SynchronousQueue PriorityBlockingQueue优先级队列 DelayQueue延迟队列应用与实战 2.同步类容器的问题 ConcurrentModificationException并发

qq_39246466的博客 656

Java多线程--并发中容器的使用之SynchronousQueue

Java 6的并发编程包中的SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除操作take,反过来也一样。 不像ArrayBlockingQueue或LinkedListBlockingQueue,SynchronousQueue内部并没有数据缓存空间,你不能调用peek()方法来看队列中是否有数据元素,因为数据

非淡泊无以明志,非宁静无以致远 432

java多线程(10)--并发类容器 CopyOnWriteArrayList 的使用

Copy-On-Write简称COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容然后再改,这是一种延时懒惰策略。从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayList和CopyOnWriteArraySet。

helongzhong的博客 395

Java中的并发容器及其用途详解

Java中的并发容器是为了支持多线程环境下的高效并发操作而设计的。在多线程编程中,经常需要对共享数据进行访问和修改,如果不使用适当的同步机制,可能会导致数据不一致或其他并发问题。Java并发容器提供了线程安全的操作,简化了多线程编程的复杂性。当我们在讨论Java中的并发容器时,还需要考虑它们如何影响性能、内存使用和并发控制策略。

wudixiuluo的博客 744

这些Java并发容器,你都了解吗?

在多线程环境下,数据的并发访问和修改是无法避免的问题。为了解决这个问题,Java 提供了一系列并发容器,这些容器在内部已经处理了并发问题,使得我们可以在多线程环境下安全地访问和修改数据。从上面的介绍总总结有以下几种容器类ConcurrentHashMap:并发版 HashMapCopyOnWriteArrayList:并发版 ArrayListCopyOnWriteArraySet:并发 SetConcurrentLinkedQueue:并发队列 (基于链表)

Venus's Blog 1万+

玩转JUC : 聊聊那些高效的并发容器

本篇文章给大家介绍了常见的并发容器相关的内容,Java并发容器提供了丰富的线程安全集合实现,适用于各种并发场景。选择合适的并发容器需要考虑多种因素,包括数据结构需求、读写比例、是否需要排序、是否需要阻塞操作等。根据实际需求选择最合适的容器类型了解所选容器的特性和限制遵循最佳实践,避免常见陷阱定期监控容器性能和内存使用情况通过合理使用并发容器,可以显著提高多线程应用程序的性能、可靠性和可维护性。

代码洁癖症患者 4769

java并发类容器

一、非同步,不安全 在我们日常编程中,容器的使用率可以说是非常高了,例如List接口下的Arraylist、Set下的HashSet、Map下的HashMap,但是这些容器都是非线程同步的,即多线程访问并不是串行执行,所以是非线程安全的。 二、同步,不完全安全 同步容器类,比如Vector,Hashtable,以及Collections.synchronize...

Oven1993的博客 317

并发容器 CopyOnWriteArrayList 学习

参考资料: 1。Java并发编程:并发容器之CopyOnWriteArrayList(转载) http://www.cnblogs.com/dolphin0520/p/3938914.html 2、聊聊并发-Java中的Copy-On-Write容器 http://ifeve.com/java-copy-on-write/...

HeatDeath的博客 233
上一篇: 多线程---详解各种锁和AQS原理
下一篇: 多线程---线程池的使用
意田天
博客等级 码龄7年 41粉丝 89原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

意田天

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

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

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

打赏作者

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

抵扣说明:

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

余额充值