JUC(二)集合类的不安全实例优化、callable、JUC3个常用辅助类


JUC(二)

集合类不安全

//java.util.ConcurrentModificationException  并发修改异常!

list不安全

并发下ArrayList不安全

package com.xiaobai.list;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

//java.util.ConcurrentModificationException  并发修改异常!
public class ListTest {
    public static void main(String[] args) {
        //List list = new ArrayList<>();
        //List list = new Vector<>();
        //List list = Collections.synchronizedList();
        List list = new CopyOnWriteArrayList<>();
        for (int i = 0; i <= 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

解决方案一:使用vector(但是不建议这种使用,因为其发布比ArrayList还早)

List list = new Vector<>();

解决方案二:使用工具类Collections,使ArrayList变的安全

List list = Collections.synchronizedList(new ArrayList<>());

解决方案三:使用JUC包下的CopyOnWriteArrayList<>();

List list = new CopyOnWriteArrayList<>();

CopyOnWrite:写入时复制,简称COW。 计算机程序设计领域的一种优化策略

原谅:多个线程调用的时候,list,读取的时候是固定的,写入的时候存在覆盖问题,所以写入的时候复制一份容器再写入(且写入操作加锁),避免写入覆盖。

set不安全

package com.xiaobai.list;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

public class SetTest {
    public static void main(String[] args) {
        //Set set = new HashSet<>();
        //Set set = Collections.synchronizedSet(new HashSet<>());
        Set set = new CopyOnWriteArraySet<>();
        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();
        }
    }
}

解决方案一:使用工具类Collections,使HashSet变的安全

Set set = Collections.synchronizedSet(new HashSet<>());

解决方案二:使用JUC包下的CopyOnWriteArraySet<>();

List list = new CopyOnWriteArraySet<>();

hashSet底层是什么?

    public HashSet() {
        map = new HashMap<>();
    }
//add set 本质就是map key 是无法重复的!
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
private static final object PRESENT = 	new Obecet();//不变的值

HashMap不安全

HashMap详解

详解

ConcurrentHasMap

package com.xiaobai.list;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class MapTest {
    public static void main(String[] args) {
        //map是这样用的嘛?默认等价于什么?//工作不用map,
        //Map map = new HashMap<>();
        //默认:HashMap map = new HashMap<>(16,0.75)
        //加载因子、初始容量
        //Map map = Collections.synchronizedMap(new HashMap());
        Map map = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();

        }
    }
}

解决方案一:使用工具类Collections,使HashMap<>()变的安全

Map map = Collections.synchronizedMap(new HashMap());

解决方案二:使用JUC包下的ConcurrentHashMap<>();

Map map = new ConcurrentHashMap<>();

Callable

与Runnable的区别:

1、可以有返回值

2、可以抛出异常

3、方法不他,一个是run(),一个是call().

使用例子:

package com.xiaobai.callable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //new Thread(new Runnable()).start();
       //因为Thread只能执行Runnable,所以Callble需要适配类 FutureTask
        MyThread myThread = new MyThread();
        FutureTask stringFutureTask = new FutureTask<>(myThread);
        new Thread(stringFutureTask,"a").start();
        new Thread(stringFutureTask,"b").start();
        String m = stringFutureTask.get();
        System.out.println(m);

    }
}

class MyThread implements Callable {
    @Override
    public String call() throws Exception {
        System.out.println("sdf");
        return "dad";
    }
}

1、get(),可能需要等待

2、2个线程只输出一句,是因为有缓存

JUC常用的辅助类

CountDownLatch

package com.xiaobai.callable;

import java.util.concurrent.CountDownLatch;

//计数器,再必须执行的任务执行时,使用
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"运行了");
                countDownLatch.countDown();//-1操作
            },String.valueOf(i)).start();
        }
        countDownLatch.await();//等待计数器归零再执行后续
        System.out.println("结束");
    }
}

CyclicBarrier

package com.xiaobai.callable;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("结束");
        });
        for (int i = 1; i <= 18 ; 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();
        }
    }
}

Semaphore

Semaphore:信号量

package com.xiaobai.callable;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDmeo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);//如果达到3个线程,等待
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();//获得线程,如果已经满了,等待
                    System.out.println(Thread.currentThread().getName()+"启动了线程");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName()+"关闭了线程");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();//释放,然后唤醒等待的线程。
                }

            },String.valueOf(i)).start();
        }
    }
}

semaphore.acquire();//获得线程,如果已经满了,等待

semaphore.release();//释放,然后唤醒等待的线程。

作用:

1、多个共享资源相互排斥使用

2、并发限流,控制最大的线程数。

相关