线程安全的定义:当多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程如何交替执行,并且在主调用代码中不需要额外的同步和协调,这个类都能表现出正确的行为,那么这个类就是线程安全的。
线程安全性可能是非常复杂的,在没有充足同步的情况下,多个线程中的操作执行顺序是不可预测的,甚至会产生非常奇怪的结果。
如果当多个线程访问一个可变的共享变量时没有使用合适的同步,那么线程就会出现错误。有三种方式可以修复这个问题:
- 不在线程间共享变量
- 将可变的变量改为不可变的变量
- 在访问时使用同步。
有这样一个需求,需要按照序列顺序产生一个序列(不能有重复数据)
自增,看似是一个原子操作实际上一个自增包含三个操作步骤:获取变量值, 将变量值加1,将计算结果写入变量。
/** * * @author zhangwei_david * @version $Id: UnsafeSequence.java, v 0.1 2014年10月24日 下午9:38:54 zhangwei_david Exp $ */ public class UnsafeSequence { public static UnsafeSequence unsafeSequence = new UnsafeSequence(); public static UnsafeSequence getInstance() { return unsafeSequence; } private int value; public int getNext() { return value++; } }
这个类在单线程下是没有任何问题的,可以顺序的生成一个序列。
/** * * @author zhangwei_david * @version $Id: OneThread.java, v 0.1 2014年10月25日 下午9:50:32 zhangwei_david Exp $ */ public class OneThread { /** * * @param args */ public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(UnsafeSequence.getInstance().getNext()); } } }
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
那么在多项线程下有是什么样的情况呢?
import java.util.concurrent.TimeUnit; /** * * @author zhangwei_david * @version $Id: Test.java, v 0.1 2014年10月24日 下午9:40:41 zhangwei_david Exp $ */ public class Test { /** * * @param args */ public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(new Runnable() { public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { } System.out.println(UnsafeSequence.getInstance().getNext()); } }).start(); } } } 0 15 18 17 21 24 28 16 14 2 0 1 13 12 5 10 31 33 37 11 6 8 9 7 41 43 40 39 38 36 34 35 45 49 58 32 30 29 27 26 25 23 22 4 20 3 19 88 87 86 85 83 84 80 82 81 79 78 77 76 89 90 74 75 73 72 71 70 94 95 69 67 68 66 65 64 63 97 62 61 59 60 57 56 55 54 53 52 51 50 48 47 46 44 42 98 96 93 92 91
我们可以发现在多线程下,打印的结果是乱序的。 是不是以此就可以断定这个类不是线程安全的内。当然不能,但因的结果是由线程调度和执行的时间决定的。 如果是线程安全的,最终的结果应该自增到99,可是最终没有自增到99,仔细查询结果可以发现有打印了连个零,也就是说有两个线程访问了同一个值,以此可以断定这个类不是线程安全的。
这是由于多线程要共享相同的内存地址空间,并且是并发运行,因此它们可能会访问或修改其他线程正在使用的变量。如果需要是共享的变量的行为可以预测就需要使用同步。如果没有使用同步,那么无论是编译器、硬件还是在运行时都可以对操作进行优化重新排序,这有助有提升性能但也为开发人员带来了负担。
那么如何将这个类改为线程安全的呢? 我们只需将获取下一个值的方法改为同步方法既可以解决这个问题。
/** * * @author zhangwei_david * @version $Id: Sequence.java, v 0.1 2014年10月24日 下午9:51:20 zhangwei_david Exp $ */ public class SafeSequence { private static class InstanceHolder { public static SafeSequence instance = new SafeSequence(); } public SafeSequence getInstance() { return InstanceHolder.instance; } private volatile int value = 0; public synchronized int getNext() { return value++; } }
相关推荐
Servlet 线程安全问题 Servlet 线程安全问题是指在使用 Servlet 编程时,如果不注意多线程安全性问题,可能会导致难以发现的错误。Servlet/JSP 技术由于其多线程运行而具有很高的执行效率,但这也意味着需要非常...
什么是线程安全? 答:线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等...
本文将深入探讨在多线程环境中使用List时遇到的非线程安全问题,并提供相应的解决方案和最佳实践。 List是.NET框架中常用的一个动态数组,它提供了方便的增删改查操作。然而,List并未设计为线程安全的容器,这意味...
在计算机编程领域,尤其是涉及到实时系统和并发编程时,线程锁和线程安全变量是至关重要的概念。LabWindows/CVI是一种流行的交互式C开发环境,特别适合于开发科学和工程应用。本实例将深入探讨如何在LabWindows/CVI...
但是需要注意,虽然这个方法可以保证基本的线程安全,但迭代仍然是非线程安全的,即不能在遍历过程中修改Map。 2. 使用ConcurrentHashMap:Java从1.5版本开始引入了ConcurrentHashMap,它是线程安全且高并发性能的...
线程安全的日志库在多线程环境下尤为重要,因为不正确的日志操作可能会导致数据竞争和同步问题。本文将详细讨论如何在C++中实现一个基于Win32接口的线程安全日志库,并关注其核心概念和技术。 首先,我们需要理解...
浅谈C#跨线程调用窗体控件引发的线程安全问题 C#跨线程调用窗体控件时可能会引发线程安全问题,例如当多个线程操作同一个控件时,该控件可能会进入不一致的状态,出现争用情况和死锁等问题。因此,确保以线程安全...
在IT行业中,尤其是在开发高并发应用时,线程安全是一个至关重要的问题。"C# 高效线程安全,解决多线程写txt日志类.zip" 提供了一个专门用于多线程环境下写入txt日志文件的解决方案,确保了在并发写入时的数据一致性...
Servlet和Struts Action是两种常见的Java Web开发组件,它们在多线程环境下运行时可能存在线程安全问题。线程安全是指在多线程环境中,一个类或者方法能够正确处理多个线程的并发访问,保证数据的一致性和完整性。 ...
在多线程环境下,线程安全的单例模式尤为重要,因为如果不正确实现,可能会导致多个线程同时创建多个实例,违反了单例模式的基本原则。 在Java中,单例模式通常有以下几种实现方式: 1. 饿汉式(静态常量): ...
操作系统课程设计中实现线程安全的双向链表是一项重要的实践任务,这涉及到多线程编程、数据结构以及并发控制等核心知识点。在这个项目中,我们主要关注如何在多线程环境下构建一个能够正确操作(如插入、删除)而不...
在C#编程中,线程安全是多线程应用程序中至关重要的一个方面,尤其是在处理共享资源如文本日志文件时。本主题将深入探讨如何在C#中创建一个高效的线程安全日志类,用于在多线程环境中安全地写入txt日志。 首先,...
在IT行业中,线程安全是多线程编程中的一个重要概念,确保多个线程并发执行时,数据的正确性和完整性不会受到影响。线程安全通常通过同步机制来实现,其中包括原子操作和锁机制。本文将深入探讨易语言中的原子锁与...
Java线程安全是多线程编程中的一个关键概念,它涉及到多个线程访问共享资源时可能出现的问题。在Java中,线程安全问题通常与并发、内存模型和可见性有关。Java内存模型(JMM)定义了如何在多线程环境下共享数据的...
在处理串口通信时,线程安全是一个关键的概念,特别是当程序需要同时处理多个任务或者从外部设备(如串口)持续接收数据时。本主题将深入探讨如何在VS2013中使用C#实现线程安全的串口数据接收。 串口通信是计算机...
线程安全的单例模式在多线程环境下尤其重要,因为不正确的实现可能导致多个线程创建多个实例,这违反了单例模式的基本原则。C++11引入了新的特性,如std::mutex和std::call_once,使得实现线程安全的单例模式变得...
在Java编程中,多线程安全集合是程序员在并发环境下处理数据共享时必须考虑的关键概念。这些集合确保了在多个线程访问时的数据一致性、完整性和安全性,避免了竞态条件、死锁和其他并发问题。Java提供了一系列的线程...
...Swoole扩展本身是一个强大的异步并发框架,尤其适用于构建高性能的网络应用,如Web服务器、...无论是Linux还是Windows,线程安全还是非线程安全,都有相应的解决方案,确保开发者可以充分利用Swoole的强大功能。
本文将深入探讨如何使用C++进行hiredis的封装,以实现线程安全的Redis客户端操作。 首先,hiredis是Redis官方提供的一个纯C语言的简单协议解析库,它专注于处理Redis命令和响应,而忽略了更高级别的抽象。为了在C++...