Java多线程编程是现代软件开发的重要组成部分,然而,多线程环境下数据的安全性一直是一个棘手的问题。本文将探讨如何通过线程安全集合来解决这一挑战。我们将深入研究Java中的Concurrent包,介绍诸如ConcurrentHashMap、CopyOnWriteArrayList等强大的数据结构,它们为多线程应用提供了高效的数据管理方式。无论您是初学者还是有经验的开发人员,都将从本文中学到如何确保数据在多线程环境下的安全性,为您的Java多线程应用程序打造坚实的基础。
示例:
public class TestCopyOnWriteArrayList {
public static void main(String[] args) {
//1创建集合
CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<>();
//2使用多线程操作
ExecutorService es=Executors.newFixedThreadPool(5);
//3提交任务
for(int i=0;i<5;i++) {
es.submit(new Runnable() {
@Override
public void run() {
for(int j=0;j<10;j++) {
list.add(Thread.currentThread().getName()+"...."+new Random().nextInt(1000));
}
}
});
}
//4关闭线程池
es.shutdown();
while(!es.isTerminated()) {}
//5打印结果
System.out.println("元素个数:"+list.size());
for (String string : list) {
System.out.println(string);
}
}
}
CopyOnWriteArrayList如何做到线程安全的
CopyOnWriteArrayList使用了一种叫写时复制的方法,当有新元素添加到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。
当有新元素加入的时候,如下图,创建新数组,并往新数组中加入一个新元素,这个时候,array这个引用仍然是指向原数组的。
当元素在新数组添加成功后,将array这个引用指向新数组。
CopyOnWriteArrayList
的整个add操作都是在锁的保护下进行的。 这样做是为了避免在多线程并发add的时候,复制出多个副本出来,把数据搞乱了,导致最终的数组数据不是我们期望的。
CopyOnWriteArrayList
的add
操作的源代码如下:
public boolean add(E e) {
//1、先加锁
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//2、拷贝数组
Object[] newElements = Arrays.copyOf(elements, len + 1);
//3、将元素加入到新数组中
newElements[len] = e;
//4、将array引用指向到新数组
setArray(newElements);
return true;
} finally {
//5、解锁
lock.unlock();
}
}
由于所有的写操作都是在新数组进行的,这个时候如果有线程并发的写,则通过锁来控制,如果有线程并发的读,则分几种情况:
1、如果写操作未完成,那么直接读取原数组的数据;
2、如果写操作完成,但是引用还未指向新数组,那么也是读取原数组数据;
3、如果写操作完成,并且引用已经指向了新的数组,那么直接从新数组中读取数据。
可见,CopyOnWriteArrayList
的读操作是可以不用加锁的。
示例:
public class TestCopyOnWriteArraySet {
public static void main(String[] args) {
//1创建集合
CopyOnWriteArraySet<String> set=new CopyOnWriteArraySet<>();
//2添加元素
set.add("pingguo");
set.add("huawei");
set.add("xiaomi");
set.add("lianxiang");
set.add("pingguo");
//3打印
System.out.println("元素个数:"+set.size());
System.out.println(set.toString());
}
}
注意⚠️: ConcurrentHashMap jdk1.8之前才是分段锁,1.8之后没有使用分段锁了,而是乐观锁加Synchronized,这里注意一下
示例:
public class TestConcurrentHashMap {
public static void main(String[] args) {
//1创建集合
ConcurrentHashMap<String, String> hashMap=new ConcurrentHashMap<String, String>();
//2使用多线程添加数据
for(int i=0;i<5;i++) {
new Thread(new Runnable() {
@Override
public void run() {
for(int k=0;k<10;k++) {
hashMap.put(Thread.currentThread().getName()+"--"+k, k+"");
System.out.println(hashMap);
}
}
}).start();
}
}
}
本期结束咱们下次再见👋~
🌊 关注我不迷路,如果本篇文章对你有所帮助,或者你有什么疑问,欢迎在评论区留言,我一般看到都会回复的。大家点赞支持一下哟~ 💗
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。