长沙Java编程并发实现需要关注哪些问题?
随着互联网的快速发展,现今Java编程开发是目前大多数软件开发程序员都在学习和使用的一种编程开发语言,而本文我们就简单来了解一下,长沙Java编程并发实现需要关注哪些问题?
随着互联网的快速发展,现今Java编程开发是目前大多数软件开发程序员都在学习和使用的一种编程开发语言,而本文我们就简单来了解一下,长沙Java编程并发实现需要关注哪些问题?
我们可以看出CopyOnWriteArrayList的核心原理就是在对数组进行增删改的时候全部都是先加独占锁,然后对原有的数组进行拷贝,然后基于新复制的数组进行操作,后将这个新的数组替换成员变量的数组;而对于读的操作来说,都是不加锁的,是基于当前成员变量的数组的这一时刻的快照来读的。其实CopyOnWriteArrayList是基于一种写时复制的思想,写的时候基于新拷贝的数组来操作,之后再赋值给成员变量,读的时候是原有的数组,这样读写其实就是不是同一个数组,这样就避免了读写冲突的情况,这其实也体现了一种读写分离的思想,读写操作的是不同的数组。
CopyOnWriteArrayList适用场景
接下来我们来思考一下,CopyOnWriteArrayList适合使用在什么样的场景中。通过上面源码的分析,我们可以看出,所有的写操作,包括增删改都需要加同一把独占锁,所以同时只允许一个线程对数组进行拷贝赋值的操作,多线程并发情况下所有的操作都是串行执行的,势必会导致并发能力降低,同时每次操作都涉及到了数组的拷贝,性能也不太好;而所有的读操作都不需要加锁,所以同一时间可以允许大量的线程同时读,并发性能高。所以综上我们可以得出一个结论,那就是CopyOnWriteArrayList适合读多写少的场景。
CopyOnWriteArrayList的局限性
说完CopyOnWriteArrayList,我们来想一想它有没有什么缺点。看起来CopyOnWriteArrayList除了写的并发性能差点,好像没有什么缺点了。的确,单从性能来看,确实是这种情况,但是,从数据一致性的角度来看,CopyOnWriteArrayList的数据一致性能力较弱,属于数据弱一致性。所谓的弱一致性,你可以这么理解,在某一个时刻,读到的数据并不是当前这一时刻新的数据。
就拿CopyOnWriteArrayList举例来说,当有个线程A正在调用add方法来添加元素,此时已经完成了数组的拷贝,并且也将元素添加到数组中,但是还没有将新的数组赋值给成员变量,此时,另一个线程B来调用CopyOnWriteArrayList的size方法,来读取集合中元素的个数,那么此时读到的元素个数其实是不包括线程A要添加的元素,因为线程A并没有将新的数组赋值给成员变量,这就导致了线程B读到的数据不是新的数据,也就是跟实际的数据不一致。
所以,从上面我们可以看出,CopyOnWriteArrayList对于数据一致性的保证,还是比较弱的。其实不光是CopyOnWriteArrayList,其实Java中的很多集合,队列的实现对于数据一致性的保证都比较弱。
如何来保证数据的强一致性
那么有什么好的办法可以保证数据的强一致性么?当然,保证并发安全,加锁就可以完成,但是加什么锁可以保证数据读写安全和数据一致性,其实简单粗暴的方法就是对所有的读写都加上同一把独占锁,这样保证所有的读写操作都是串行执行,那么读的时候,其他线程一定不能写,那么读的一定是新的数据。
如果真的这么去加独占锁,的确能够保证读写安全,但是性能却会很差,这也是为什么CopyOnWriteArrayList的读不加锁的原因,其实CopyOnWriteArrayList在设计的时候,就是降低数据一致性来换取读的性能。
那有没有什么折中的方法,既能保证读的性能不差,又能保证数据强一致性呢。这时就可以用读写锁来实现。所谓的读写锁,就是写的时候,其他线程不能写也不能读,读的时候,其他线程能读,但是不能写。也就是写写、读写互斥,但是读读不互斥。基于这种方式,就能保证读的时候,一定没有人在写,这样读到的数据就一定是新的,同时也能保证其他线程也能读,不会出现上面举例的那种情况了,也就能保证数据的强一致性。读写锁相比独占锁而言,大大提高了读的并发能力,但是写的时候不能读,相比于CopyOnWriteArrayList而言,读的并发能力有所降低,这可能就是鱼(并发性能)和熊掌(数据一致性)不可兼得吧。