java 加减 线性安全_JUC基础之集合类不安全,Callable接口,CountDownLatch,CyclicBarrier,Semaphore常用辅助类,ReadWRiteLock读写锁...

List

public class UnsafeList

{

public static void main(String[] args)

{

List list = new ArrayList<>();

for (int i = 1; i <= 10; i++)

{

new Thread(()->{

list.add(UUID.randomUUID().toString().substring(0,5));

System.out.println(list);

},"Thread"+i).start();

}

}

}

运行结果:

[2fbaf]

[2fbaf, f9622, 08fdf, 1399d, ba2a9, f0b84]

[2fbaf, f9622, 08fdf, 1399d, ba2a9]

[2fbaf, f9622, 08fdf, 1399d, ba2a9, f0b84, 1b25a, efb3d, 32159]

[2fbaf, f9622, 08fdf, 1399d]

[2fbaf, f9622, 08fdf]

[2fbaf, f9622]

[2fbaf]

[2fbaf, f9622, 08fdf, 1399d, ba2a9, f0b84, 1b25a]

Exception in thread "Thread7" java.util.ConcurrentModificationException

at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)

at java.util.ArrayList$Itr.next(ArrayList.java:859)

at java.util.AbstractCollection.toString(AbstractCollection.java:461)

at java.lang.String.valueOf(String.java:2994)

at java.io.PrintStream.println(PrintStream.java:821)

at com.y1.list.UnsafeList.lambda$main$0(UnsafeList.java:27)

at java.lang.Thread.run(Thread.java:748)

并发下ArrayList是不安全的

ConcurrentModificationException: 并发修改异常

解决方案一:Vector

//解决方案一

List list = new Vector<>();

for (int i = 1; i <= 10; i++)

{

new Thread(()->{

list.add(UUID.randomUUID().toString().substring(0,5));

System.out.println(list);

},"Thread"+i).start();

}

Vector在jdk 1.0就出来了,而ArrayList在jdk 1.2才出来的

所以:选用Vector代替ArrayList不是最好的方案

解决方案二:Collections.synchronizedList(new ArrayList<>())

//解决方案二

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

for (int i = 1; i <= 10; i++)

{

new Thread(()->{

list.add(UUID.randomUUID().toString().substring(0,5));

System.out.println(list);

},"Thread"+i).start();

}

解决方案三:new CopyOnWriteArrayList<>();

写入时复制,这是一种COW思想,一种计算机设计优化策略

多线程调用的时候,List读取的时候是读取固定的,写入的时候复制一份,复制完给调用者,调用者写完再给他放回去,写入时避免覆盖,造成数据问题(一种读写分离的思想)

//解决方案三

List list = new CopyOnWriteArrayList<>();

for (int i = 1; i <= 10; i++)

{

new Thread(()->{

list.add(UUID.randomUUID().toString().substring(0,5));

System.out.println(list);

},"Thread"+i).start();

}

看一下 CopyOnWriteArrayList 的底层 注意这个 volatile

/** The array, accessed only via getArray/setArray. */

private transient volatile Object[] array;

为什么要用CopyOnWriteArrayList?

先看一下Vector的源码

public synchronized void addElement(E obj) {

modCount++;

ensureCapacityHelper(elementCount + 1);

elementData[elementCount++] = obj;

}

只要有Synchronized的方法效率会非常低,同理Collections.synchronizedList(new ArrayList<>())也是

看一下CopyOnWriteArrayList的源码

public boolean add(E e) {

final ReentrantLock lock = this.lock;

lock.lock();

try {

Object[] elements = getArray();

int len = elements.length;

Object[] newElements = Arrays.copyOf(elements, len + 1); //复制

newElements[len] = e;

setArray(newElements); //写回

return true;

} finally {

lock.unlock();

}

}

Set

先看一下这个图,List,Set是同级的,是相似的,(还要注意一个同级的BlockingQueue 阻塞队列)

300240c9e916

Set的多线程不安全问题以及解决方案

public class UnsafeSet

{

//HashSet set = new HashSet<>(); 出现ConcurrentModificationException

//解决方案一 Collections.synchronizedSet(new HashSet<>());

//解决方案二 CopyOnWriteArraySet set = new CopyOnWriteArraySet();

public static void main(String[] args)

{

HashSet 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();

}

}

}

HashSet底层是什么?看源码

public HashSet() {

map = new HashMap<>();

}

看一下HashSet的add方法

public boolean add(E e) {

return map.put(e, PRESENT)==null;

}

本质就是map的key,因为map的key是无法重复的

PRESENT:是一个不变的的值

// Dummy value to associate with an Object in the backing Map

private static final Object PRESENT = new Object();

HashMap

还是要先看看源码,简单了解一下HashMap

public HashMap(int initialCapacity) {

this(initialCapacity, DEFAULT_LOAD_FACTOR);

}

public HashMap() {

this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted

}

loadFactor:加载因子

initialCapacity:初始容量

HashMap默认

300240c9e916

验证不安全:

public static void main(String[] args)

{

HashMap hashMap = new HashMap<>();

for (int i = 1; i <= 30; i++)

{

new Thread(()->{

hashMap.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,2));

System.out.println(hashMap);

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

}

}

报出并发修改异常 ConcurrentModificationException

解决方案一

Collections.synchronizedMap(new HashMap<>());

发现HashMap没有刚才的CopyOnWrite

看文档

300240c9e916

发现了一个 ConcurrentHashMap

解决方案二

Map hashMap = new ConcurrentHashMap<>();

Callable

public interface Callable

返回结果并可能引发异常的任务。 实现者定义一个没有参数的单一方法,称为call 。

Callable接口类似于Runnable ,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,A Runnable不返回结果,也不能抛出被检查的异常。

1.可以有返回值

2.可以抛出异常

3.和Runnable方法不同: Runnable:run() Callable:call()

//Runnable

@Override

public void run()

{

}

//Callable

@Override

public Object call() throws Exception

{

return null;

}

看一下Callable的源码

@FunctionalInterface

public interface Callable {

/**

* Computes a result, or throws an exception if unable to do so.

*

* @return computed result

* @throws Exception if unable to compute a result

*/

V call() throws Exception;

}

范型的参数就是是方法的返回值

但是new Thread() 只能接收Runnable作为参数

所以怎么用?

public class TestCallable

{

public static void main(String[] args)

{

new Thread(new FutureTask(new MyThread())).start();

}

}

class MyThread implements Callable

{

@Override

public String call() throws Exception

{

return "null";

}

}

为什么能这么用呢?

300240c9e916

适配器思想,FutureTask当作一个适配类

public class TestCallable

{

public static void main(String[] args) throws ExecutionException, InterruptedException

{

MyThread thread = new MyThread();

FutureTask futureTask = new FutureTask(thread); //适配类

new Thread(futureTask,"Y1").start();

new Thread(futureTask,"Y2").start(); //结果会被缓存,效率高

Object o = (String)futureTask.get(); //获取Callable返回结果,肯能会产生阻塞

System.out.println(o);

}

}

class MyThread implements Callable

{

@Override

public String call() throws Exception

{

System.out.println("Y1”);

return "Callable";

}

}

futureTask.get();

可能会产生阻塞,因为它需要等待return,如果你的业务方法耗时太久太会一直等,我们要使用异步通信来处理,或者放到代码最后面...

多线程下结果会被缓存,效率高

CountDownLatch 减法计数器

// 减法计数器

public class Demo

{

public static void main(String[] args) throws InterruptedException

{

CountDownLatch countDownLatch = new CountDownLatch(5); //总数是5

for (int i = 0; i <5; i++)

{

new Thread(()->{

System.out.println(Thread.currentThread().getName()+"被淘汰");

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

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

}

countDownLatch.await(); //等待计数器归零 再向下执行 否则会一直等待

System.out.println("所有人都被淘汰");

}

}

原理:

countDownLatch.countDown();

countDownLatch.await();

每次有线程调用countDown时 数量-1。计数器变为0时countDownLatch.await(); 会被唤醒,执行之后的代码,否则等待唤醒

CyclicBarrier 加法计数器

//加法计数器

public class Demo

{

public static void main(String[] args)

{

//消费十次免单

CyclicBarrier cyclicBarrier = new CyclicBarrier(10,()->{ // params:数量 , Runnable

System.out.println("消费10次免单");

});

for (int i = 1; i <=10; 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 信号量

public class Demo

{

public static void main(String[] args)

{

Semaphore semaphore = new Semaphore(3); //许可证数量

for (int i = 1; i <7 ; i++)

{

new Thread(()->{

try

{

semaphore.acquire(); //acquire() 获得

System.out.println(Thread.currentThread().getName()+"抢到许可证");

TimeUnit.SECONDS.sleep(1);

System.out.println(Thread.currentThread().getName()+"释放许可证");

}

catch (InterruptedException e)

{

e.printStackTrace();

}finally

{

semaphore.release(); //release() 释放

}

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

}

}

}

运行结果:

1抢到许可证

3抢到许可证

2抢到许可证

3释放许可证

1释放许可证

2释放许可证

5抢到许可证

4抢到许可证

6抢到许可证

5释放许可证

4释放许可证

6释放许可证

原理:

acquire() 获得许可证 ,如果许可证全部被拿走,等待到释放为止

release() 释放当前信号量,会使当前可用信号量+1,然后唤醒等待线程

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

ReadWRiteLock 读写锁(共享锁,独占锁)

300240c9e916

读可以被多线程同时读,写的时候只能一个线程写,提高了效率

public class Demo

{

public static void main(String[] args)

{

MyCache cache = new MyCache();

//写入

for (int i = 1; i <6 ; i++)

{

final int temp = i;

new Thread(()->{

cache.put(temp+"",temp+"");

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

}

//读取

for (int i = 1; i <6 ; i++)

{

final int temp = i;

new Thread(()->{

cache.get(temp+"");

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

}

}

}

/*

自定义缓存

*/

class MyCache

{

private volatile Map map = new HashMap<>();

//读写锁,更加细粒度的控制

private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

//存,写

public void put(String key , Object value)

{

readWriteLock.writeLock().lock(); //加写锁

try

{

System.out.println(Thread.currentThread().getName() + "写入" + key);

map.put(key,value);

System.out.println(Thread.currentThread().getName() + "写入OK");

}

catch (Exception e)

{

e.printStackTrace();

}

finally

{

readWriteLock.writeLock().unlock(); //解锁

}

}

//取,读

public void get(String key)

{

readWriteLock.readLock().lock(); //加读锁 为了防止在写之前就读 也是为了读的时候不存在写

try

{

System.out.println(Thread.currentThread().getName() + "读取" + key);

Object o = map.get(key);

System.out.println(Thread.currentThread().getName() + "读取"+o+"ok");

}

catch (Exception e)

{

e.printStackTrace();

}

finally

{

readWriteLock.readLock().unlock(); //解锁

}

}

}

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、下4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、下4载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值