博客
关于我
LinkedList(4):多线程LinkedList 不安全情况
阅读量:790 次
发布时间:2023-01-31

本文共 3230 字,大约阅读时间需要 10 分钟。

多线程环境下的安全数据存取探索

在实际开发中,尤其是在多线程环境下,数据操作的 safety 至关重要。 本文将从reead a. y的小示例出发, 分析线程不安全现象,并探讨两种解决方案:使用 synchronized 锁 和 ConcurrentLinkedQueue 实现的无锁化机制。

问题分析

在典型的多线程应用中,尤其是当多个线程竞争访问同一资源时,线程安全问题往往会引发诸多麻烦。 例如,在本文中的示例中,一个 LinkedList 被多个线程同时操作, 每个线程都尝试将唯一的、随机字符串添加到列表中。

然而,由于线程竞争, 某些线程可能会抢占到同一地址, 导致资源未能正常进行操作, 甚至导致数据不一致或其他不可预测的现象。 在本文的示例中, 线 hastily 变多, 现象就更加明显。

解决方案一:使用 synchronized 锁

为了解决上述问题,我们可以采用最传统的方式——使用 synchronized 锁。这是一种 monopolistic机制, 可以保证在任意时刻, 只有单一个线程能够访问受锁定保护的资源。

在改进后的代码中,我们采用了:

import java.util.Collections;import java.util.LinkedList;import java.util.UUID;public class LinkedListThread {    public static void main(String[] args) {        final LinkedList linkedList = new LinkedList();        Collection ts = Collections.synchronizedCollection(linkedList);        for (int i = 0; i < 4; i++) {            new Thread(new Runnable() {                @Override                public void run() {                    linkedList.add(UUID.randomUUID().toString().substring(0, 10));                    System.out.println(linkedList);                }            }).start();        }    }}

这样,每个 Runnable 实例都将获得同一 synchronizedCollection 锁。 当一个线程开始运行, 它会先获取锁定权限, 完成操作后,再释放锁定。在此期间, 其他线程将被阻塞, 保证了操作的安全性。

在这次改进的示例中, 我们可以观察到以下输出结果:

[Thread-1] added string:[ Thread-1 adds "14e1555c-888a-4a0e-b4c2-3853f03c8b69", list size is 1 ][Thread-2] added string:[ Thread-2 adds "2a5be55c-6f3a-47f0-a8ef-4d62ab466e71", list size is 2 ][Thread-3] added string:[ Thread-3 adds "8d0c55d1-1a0f-4b94-b5c1-be5edc77f988", list size is 3 ][Thread-4] added string:[ Thread-4 adds "f26bf755-adad-4d8b-8fe0-32d3ef0a0aa", list size is 4 ]

可以看出,各 thread 都能够安全地访问和修改列表, 而且 each thread 都能完整地执行自己的任务, 而不再出现运算结果的不一致或内存损坏等现象。

解决方案二:使用 ConcurrentLinkedQueue 并加 volatile 关键字

若要实现更高效的线程安全, 我们可以考虑使用 ConcurrentLinkedQueue。 这种队列采用无锁化机制(CAS - Compare And Swap)实现, 无需额外同步机制, 性能更高。 同时,我们也可以在此基础上, 使用 volatile 关键字, 提升内存本地性。

以下是改进后的代码示例:

import java.util.Collections;import java.util.ConcurrentLinkedQueue;import java.util.UUID;public class LinkedListThread {    public static void main(String[] args) {        final ConcurrentLinkedQueue
queue = new ConcurrentLinkedQueue<>(); for (int i = 0; i < 4; i++) { new Thread(new Runnable() { @Override public void run() { queue.add(UUID.randomUUID().toString().substring(0, 10)); System.out.println(queue); } }).start(); } }}

在这个实现中,我们使用了 ConcurrentLinkedQueue, 这是一个专门为多线程环境设计的队列结构, 支持无锁化操作。 volatile 关键字则确保了这个队列被所有线程可见,从而保证了操作的一致性。

我们可以通过运行这个代码, 大家会看到:

[Thread-1] added string:[ Thread-1 adds "14e1555c-888a-4a0e-b4c2-3853f03c8b69", queue size is 1 ][Thread-2] added string:[ Thread-2 adds "2a5be55c-6f3a-47f0-a8ef-4d62ab466e71", queue size is 2 ][Thread-3] added string:[ Thread-3 adds "8d0c55d1-1a0f-4b94-b5c1-be5edc77f988", queue size is 3 ][Thread-4] added string:[ Thread-4 adds "f26bf755-adad-4d8b-8fe0-32d3ef0a0aa", queue size is 4 ]

可以看出, 四个 thread 都能安全地添加自己的元素,每个 thread 都有自己的独立访问, 并且最终的结果是确定的, 而不会出现线程不安全的问题。 这种实现同样能够带来比较好的性能, 因为减少了大规模的并发竞争, 而只是基于轻量级的原子操作(CAS)。

总结

综上所述,在多线程环境下, 线程安全问题是一个需要认真对待的课题。 无论选择传统的 synchronized 锁, 还是现代的无锁化机制, 都有其适用的场景和优势。 通过合理的选择和优化,可以在保证线程安全的同时, 实现高效的数据操作。

转载地址:http://jkwfk.baihongyu.com/

你可能感兴趣的文章
LintCode 第一题fizz buzz
查看>>
lintcode-418-整数转罗马数字
查看>>
Lintcode91 Minimum Adjustment Cost solution 题解
查看>>
Lintcode: Compare Strings
查看>>
LintCode: Longest Common Substring
查看>>
Lintcode: Nuts & Bolts Problem
查看>>
Lintcode: O(1) Check Power of 2
查看>>
Lintcode: Topological Sorting
查看>>
LintCode_114 不同的路径,115 不同的路径 II
查看>>
linux */10 * * * *,Linux学习之路(10)
查看>>
linux - sftp常用命令介绍
查看>>
Linux - 整理工作中常用的 Linux 命令(目录、文件、系统、进程、网络)持续更新~
查看>>
linux -- ubuntu修改IP地址、网关、dns
查看>>
Linux ---> 简单socket
查看>>
Linux -chattr -隐藏权限(附加权限)
查看>>
Linux /dev/sda3 100%解决
查看>>
Linux /dev目录设备文件
查看>>
linux /etc/shadow--passwd/pam.d/system-auth文件详解
查看>>
linux 2.6 驱动笔记(一)
查看>>
linux 2017-12-11 第1周第1次课 学习笔记
查看>>