JUC进阶1---不安全集合类和常用辅助类

1、集合类不安全

1.1、List 不安全

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

public class ListTest {
    
    
    public static void main(String[] args) {
    
    
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            new Thread(() -> {
    
    
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(list);
            }).start();
        }

    }
}

在这里插入图片描述

当多个线程对一个不安全List进行写操作的时候会造成 java.util.ConcurrentModificationException 并发修改异常!

解决方案:

  1. List<String> list = new Vector<>();
    

    在这里插入图片描述

    Vector底层的add方法是加了synchronized修饰的,所以的安全的

  2. List<String> list = Collections.synchronizedList(new ArrayList<>());
    
  3. List<String> list = new CopyOnWriteArrayList<>();
    

在这里插入图片描述
CopyOnWriteArrayList 底层使用的是Lock锁,在写入的时候避免覆盖,造成数据问题!

1.2、Set 不安全

public class SetTest {
    
    
    public static void main(String[] args) {
    
    
        Set<String> set = new HashSet<>();
        for (int i = 0; i < 10; i++) {
    
    
            new Thread(() -> {
    
    
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }
}

在这里插入图片描述

解决:

  1. Set<String> set = Collections.synchronizedSet(new HashSet<>());
    
  2. Set<String> set = new CopyOnWriteArraySet<>();
    
  • HashSet的底层是什么?
public HashSet() {
    
    
        map = new HashMap<>();   // HashSet底层就是一个HashMap
    }						     // set 本质就是 map 的 key,因为map的key是无法重复的
public boolean add(E e) {
    
    
        return map.put(e, PRESENT)==null;
    }
private static final Object PRESENT = new Object();  // key是set的值,value是一个常量

1.3、Map 不安全

public class MapTest {
    
    
    public static void main(String[] args) {
    
    
        Map<String, String> map = new HashMap<>();
        for (int i = 0; i < 10; i++) {
    
    
            new Thread(() -> {
    
    
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }
}

在这里插入图片描述

解决:

  1. Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
    
  2. Map<String, String> map = new ConcurrentHashMap<>();
    

在这里插入图片描述

2、Callable

在这里插入图片描述

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同,run() / call()
    在这里插入图片描述
public class TestCallable {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
    
    
            @Override
            public Integer call() throws Exception {
    
    
                System.out.println("调用了call方法");
                return 1024;
            }
        });
        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start();  打印一次,同一个任务结果会被缓存,效率高
        System.out.println(futureTask.get());
    }
}

在这里插入图片描述

细节:1、有缓存

​ 2、结果要执行完才有,所以可能需要等待,会阻塞!

3、常用的辅助类

3.1、CountDownLatch—减法计数器

public class CountDownLatchDemo {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        // 总数是6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
    
    
            new Thread(() -> {
    
    
                System.out.println(Thread.currentThread().getName() + "执行完毕");
                countDownLatch.countDown();     // 数量 - 1
            }, String.valueOf(i)).start();
        }
        countDownLatch.await();  //等待计数器归零,然后再向下执行
        System.out.println("关机");
    }
}

原理:

  • countDownLatch.countDown(); // 数量 - 1

  • countDownLatch.await(); //等待计数器归零,然后再向下执行

每次有线程调用, countDown() 数量-1,假设计数器变为0,countDownLatch.await() 就会被唤醒,继续执行!

3.2、CyclicBarrier—加法计数器

public class CyclicBarrierDemo {
    
    
    public static void main(String[] args) {
    
    
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
    
    
            System.out.println("召唤神龙");
        });
        for (int i = 1; i <= 9; i++) {
    
    
            final int temp = i;
            new Thread(() -> {
    
    
                System.out.println(Thread.currentThread().getName() + "收集了第" + temp +"个龙珠");
                try {
    
    
                    cyclicBarrier.await();  //等待线程数量达到就执行创建时设置的任务
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
    
    
                    e.printStackTrace();
                }
            }).start();
        }
    }

3.3、Semaphore—信号量

public class SemaphoreDemo {
    
    
    public static void main(String[] args) {
    
    
        //资源个数,允许线程访问的数量! 限流~
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 6; i++) {
    
    
            new Thread(() -> {
    
    
                try {
    
    
                    semaphore.acquire();   // 获得资源---P 操作
                    System.out.println(Thread.currentThread().getName() + "抢到了资源");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + "放弃了资源");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                } finally {
    
    
                    semaphore.release();  // 释放资源---V 操作
                }
            }).start();
        }
    }
}

原理:

  • semaphore.acquire(); // 获得资源—P 操作,如果已经满了就等待

  • semaphore.release(); // 释放资源—V 操作

作用:多个共享资源互斥的使用!并发限流,控制最大的线程数!

4、ReadWriteLock—读写锁

/**
 * 独占锁(写锁) 一次只能被一个线程占有
 * 共享锁(读锁) 多个线程可以同时占有
 * ReadWriteLock
 * 读-读 可以共存!
 * 读-写 不能共存!
 * 写-写 不能共存!
 */
class MyCacheLock {
    
    
    private volatile List<String> list = new ArrayList<>();
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    public void add(String value) {
    
       
        readWriteLock.writeLock().lock();  // 写锁,每次只能有一个线程写
        try {
    
    
            System.out.println(Thread.currentThread().getName() + "写入" +value);
            list.add(value);
            System.out.println(Thread.currentThread().getName() + "写入完毕!");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            readWriteLock.writeLock().unlock();
        }
    }
    public void get(int i) {
    
    
        readWriteLock.readLock().lock();   //读锁,所有线程都可以访问
        try {
    
    
            System.out.println(Thread.currentThread().getName() + "读取" + list.get(i));
            TimeUnit.SECONDS.sleep(2);
            System.out.println(Thread.currentThread().getName() + "读取完毕!");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            readWriteLock.readLock().unlock();
        }
    }
}
public class ReadWriteLockDemo {
    
    
    public static void main(String[] args) {
    
    
        MyCacheLock myCacheLock = new MyCacheLock();
        for (int i = 0; i < 5; i++) {
    
    
            final int temp = i;
            new Thread(() -> {
    
    
                myCacheLock.add(temp + "hello");
            }, String.valueOf(i)).start();
        }
        for (int i = 0; i < 5; i++) {
    
    
            final int temp = i;
            new Thread(() -> {
    
    
                myCacheLock.get(temp);
            }, String.valueOf(i)).start();
        }
    }
}

在这里插入图片描述

5、阻塞队列—BlockingQueue

在这里插入图片描述

5.1、四组API

方式 抛出异常 有返回值,不抛出异常 阻塞等待 超时等待
添加 add offer put offer
移除 remove poll take poll
检测队首元素 element peek
  • 抛出异常

    public class BlockingQueueDemo {
          
          
        public static void main(String[] args) {
          
          
            // 队列的大小
            ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
            System.out.println(blockingQueue.add("a"));
            System.out.println(blockingQueue.add("b"));
            System.out.println(blockingQueue.add("c"));
            //  IllegalStateException 异常
            System.out.println(blockingQueue.add("d"));
            
            System.out.println(blockingQueue.remove());
            System.out.println(blockingQueue.remove());
            System.out.println(blockingQueue.remove());
            // NoSuchElementException 异常
            System.out.println(blockingQueue.remove());
        }
    }
    
  • 有返回值,没有异常

    public class BlockingQueueDemo {
          
          
        public static void main(String[] args) {
          
          
            // 队列的大小
            ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
            System.out.println(blockingQueue.offer("a"));
            System.out.println(blockingQueue.offer("b"));
            System.out.println(blockingQueue.offer("c"));
            System.out.println(blockingQueue.offer("d"));  //没有异常,返回 false
    
            System.out.println(blockingQueue.poll());
            System.out.println(blockingQueue.poll());
            System.out.println(blockingQueue.poll());
            System.out.println(blockingQueue.poll());    //没有异常,返回 null
        }
    }
    
  • 阻塞等待

    public class BlockingQueueDemo {
          
          
        public static void main(String[] args) throws InterruptedException {
          
          
            // 队列的大小
            ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
            blockingQueue.put("a");
            blockingQueue.put("b");
            blockingQueue.put("c");
            blockingQueue.put("d");  //队列没有位置了,会一直阻塞,直到有元素被取出
    
            System.out.println(blockingQueue.take());
            System.out.println(blockingQueue.take());
            System.out.println(blockingQueue.take());
            System.out.println(blockingQueue.take());  //队列没有元素了,会阻塞,直到有元素进来
        }
    }
    
  • 超时等待

    public class BlockingQueueDemo {
          
          
        public static void main(String[] args) throws InterruptedException {
          
          
            // 队列的大小
            ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
            blockingQueue.offer("a");
            blockingQueue.offer("b");
            blockingQueue.offer("c");
            blockingQueue.offer("d", 2, TimeUnit.SECONDS);  //等待2秒,没有位置就退出
            System.out.println(blockingQueue.poll());
            System.out.println(blockingQueue.poll());
            System.out.println(blockingQueue.poll());
            blockingQueue.poll(2, TimeUnit.SECONDS);  // 等待两秒,没有数据来就退出
        }
    }
    

5.2、SynchronizedQueue—同步队列

  • SynchronizedQueue不能存储元素,put了一个值,必须从里面take出来,否者不能put进去值
public class SynchronizedQueueDemo {
    
    
    public static void main(String[] args) {
    
    
        SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
        new Thread(() -> {
    
    
            try {
    
    
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "放入了 1");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "放入了 2");
                synchronousQueue.put("3");
                System.out.println(Thread.currentThread().getName() + "放入了 3");
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }, "A").start();
        new Thread(() -> {
    
    
            try {
    
    
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "取出了 " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "取出了 " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "取出了 " + synchronousQueue.take());
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }, "B").start();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42372017/article/details/109544423