当前位置: 首页 > 科技观察

CopyOnwrite明白吗?

时间:2023-03-12 06:57:53 科技观察

本文转载自微信公众号《Java极客技术》,作者鸭血范。转载本文请联系Java极客技术公众号。CopyOnWrite的概念,看字面意思就可以看出。就是写的时候抄。说得轻松点,写的时候抄。它是如何实现的?先说说思路,如何实现,然后再分析一下CopyOnWrite的思路。:向容器中添加元素时,并不是直接在当??前容器中添加,而是复制一个新的容器,在新的容器中添加元素。添加后,原来容器的引用指向了新的。还记得以前提到数据库的时候,一般说的是主从复制,读写分离吗?CopyOnWrite的设计思路是不是和我们常说的主从复制,Withdrawal等读写分离一样?优点和缺点理解了概念之后,应该更容易理解它的优点和缺点。好处是读写可以并行执行,因为读的是原容器,写的是新容器。没有影响,所以读写可以并行执行。在一些高并发场景下,可以提高程序的响应时间。但是,大家可以看到,CopyOnWrite在写的时候复制了一个新的容器出来,所以我们不得不考虑它的内存开销,又回到了学习算法时强调过的一个思想:需要注意用空间换取时间,它只保证数据的最终一致性。因为读取的时候,读取的内容是原来容器中的内容,新增的内容是读取不到的。基于它的优缺点,应该可以得出一个结论:CopyOnWrite适用于写操作很少的场景,而且也能容忍读写暂时的不一致。如果你的应用场景不适合,那就考虑用其他的方法来实现。另外需要注意的是:写的时候,会复制一个新的容器,所以如果有写的需求,最好分批写,因为每次写完容器,都会复制一次容器。如果可以减少写入次数,就可以减少容器的副本数。JUC包下实现了CopyOnWrite的思想。CopyOnWriteArrayList&CopyOnWriteArraySet这两个方法,本文重点讲解清楚CopyOnWriteArrayListCopyOnWriteArrayList在CopyOnWriteArrayList中,需要注意的是add方法:publicbooleanadd(Ee){finalReentrantLocklock=this.lock;//写的时候需要locked,如果不加锁,多线程场景下可能会复制n份//加锁后,可以保证只有一个线程在操作lock.lock();try{Object[]elements=getArray();intlen=elements.length;//复制原数组Object[]newElements=Arrays.copyOf(elements,len+1);//将要添加的元素添加到新数组newElements[len]=e;//将原数组的引用指向新数组setArray(newElements);returntrue;}finally{lock.unlock();}}写的时候需要加锁,读的时候不需要加,因为的元素读取原始数组,这对新数组没有影响。添加锁会增加性能开销。publicstaticintclientTotal=5000;//并发执行线程数publicstaticintthreadTotal=200;privatestaticListlist=newArrayList<>();publicstaticvoidmain(String[]args)throwsException{ExecutorServiceexecutorService=Executors.newCachedThreadPool();finalSemaphoresemaphore=newSemaphore(threadTotal);finalCountDownLatchcountDownLatch=newCountDownLatch(对于=0;我{try{semaphore.acquire();update(count);semaphore.release();}catch(Exceptione){log.error("异常",e);}countDownLatch.countDown();});}countDownLatch.await();executorService.shutdown();log.info("size:{}",list.size());}privatestaticvoidupdate(inti){list.add(i);}}客户端请求5000次以上,200个线程同时请求。我使用ArrayList实现。我们看打印结果:如果是线程安全的,那么最后的结果应该是5000,多运行几次就会发现每个程序的执行结果都不一样。如果是CopyOnWriteArrayList呢?@Slf4jpublicclassCopyOnWriteArrayListExample{//总请求数publicstaticintclientTotal=5000;//同时并发执行的线程数publicstaticintthreadTotal=200;privatestaticListlist=newCopyOnWriteArrayList<>();publicstaticvoidmain(String[]args)throwsException{ExecutorServiceexecutorService=Executors.newCachedThreadPool();finalSemaphoresemaphore=newSemaphore(threadTotal);finalCountDownLatchcountDownLatch=newCountDownLatch(clientTotal);for({try{semaphore.acquire();update(count);semaphore.release();}catch(Exceptione){log.error("异常",e);}countDownLatch.countDown();});}countDownLatch.await();executorService.shutdown();log.info("size:{}",list.size());}privatestaticvoidupdate(inti){list.add(i);}}多次运行,结果都是一样的:可见CopyOnWriteArrayList是线程安全的