Java并发编程(三)概念介绍

在构建稳健的并发程序时,必须正确使用线程和锁。但是这终归只是一些机制。要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的(Shared)可变的(Mutable)状态的访问。

对象的状态是指存储在状态变量(例如实例或静态域)中的数据。

对象的状态可能包括其他依赖对象的域。比如某个HashMap的状态不仅是HashMap对象本身,还存储在许多Map.Entry对象中。

"共享"意味着变量可以由多个线程同时访问,而"可变"则意味着变量的值在其生命周期内可以变化。

线程安全性在于如何防止数据上发生不可控的并发访问

一个对象是否需要是线程安全的,取决于它是否被多个线程访问。

要使对象是线程安全的,需要采用同步机制来协同对对象可变状态的访问。如果无法实现协同,那么可能导致数据破坏以及其他不该出现的结果。

协同多个线程对变量访问的同步机制主要有:

1. 关键字synchronized

2. 关键字volatile

3. 显式锁(Explicit Lock)

4. 原子变量

协同多线程访问一个可变的状态变量的方法有:

1. 不在线程之间共享该状态变量

2. 将状态变量修改为不可变的变量

3. 在访问状态变量时使用同步

什么是线程安全?

一个类在多线程环境下被访问,这个类始终能表现出正确的行为,那么就称这个类是线程安全的。

在线程安全类中往往都封装了必要的同步机制,因此客户端无须进一步采取措施。

无状态的对象一定是线程安全的。

无状态可以极大降低类在实现线程安全性时的复杂性。只有当类在保存一些信息的时候,线程安全才会成为一个问题。

原子性

当一个操作被CPU无可分割地执行时就是原子操作。

典型的非原子性操作就是a++,但是实际上这是一个"读取—修改—写入"的操作序列,并且结果状态依赖于之前的状态。

在并发编程中,由于不恰当的执行顺序而出现不正确的结果是一种非常重要的情况,就是竞态条件(Race Condition)。

竞态条件

当某个计算的正确性取决于多个线程的交替执行时序时,那么就会发生竞态条件。最常见的竞态条件就是"先检查后执行(Check-Then-Act)"操作,即通过一个可能失效的观测结果来决定下一步的动作。比如延迟初始化的单例模式

复合操作

要避免竞态条件的问题,就必须在某个线程修改该变量时,通过某种方式防止其他线程使用这个变量,从而确保其他线程只能在修改操作完成或者之后读取和修改状态,而不是在修改状态的过程中。为了确保安全性,"先检查后执行"(例如延迟初始化)和"读取—修改—写入"(如递增运算)等必须是原子的。我们把"先检查后执行"(例如延迟初始化)和"读取—修改—写入"等操作统称为复合操作:包含了一组必须以原子方式执行的操作以确保线程安全性。

复合操作

加锁机制

内置锁

同步代码块

重入

时间: 2024-10-09 03:01:16

Java并发编程(三)概念介绍的相关文章

Java并发编程三个性质:原子性、可见性、有序性

并发编程 并发程序要正确地执行,必须要保证其具备原子性.可见性以及有序性:只要有一个没有被保证,就有可能会导致程序运行不正确 线程不安全在编译.测试甚至上线使用时,并不一定能发现,因为受到当时的CPU调度顺序,线程个数.指令重排的影响,偶然触发 线程安全的定义 比如说一个类,不论通过怎样的调度执行顺序,并且调用处不用对其进行同步操作,其都能表现出正确的行为,则这个类就是线程安全的 并发编程三个概念 原子性: 一个操作或多个操作要么全部执行且执行过程不被中断,要么不执行 可见性: 多个线程修改同一

java并发编程基础概念

1.什么是进程和线程 1.1 进程是程序运行资源分配的最小单位 进程是操作系统进行资源分配的最小单位,其中资源包括:CPU.内存空间.磁盘IO等,同一进程中的多个线程共享该进程中的全部系统资源,而进程和进程之间是相互独立的.进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 进程是程序在计算机上的一次执行活动.当你运行一个程序,你就启动了一个进程.显然,程序是死的.静态的,进程是活的.动态的.进程可以分为系统进程和用户进程.凡是用于完成操作

Java并发编程核心概念一览

并行相关概念 同步和异步 同步和异步通常来形容一次方法的调用.同步方法一旦开始,调用者必须等到方法结束才能执行后续动作:异步方法则是在调用该方法后不必等到该方法执行完就能执行后面的代码,该方法会在另一个线程异步执行,异步方法总是伴随着回调,通过回调来获得异步方法的执行结果. 并发和并行 很多人都将并发与并行混淆在一起,它们虽然都可以表示两个或者多个任务一起执行,但执行过程上是有区别的.并发是多个任务交替执行,多任务之间还是串行的:而并行是多个任务同时执行,和并发有本质区别. 对计算机而言,如果系

Java并发编程-基础概念全解

1.基础 1.1.什么是进程和线程 进程和线程都是操作系统所运行的程序运行的基本单元.进程可以说是是线程的集合. 进程:从系统资源讲,进程都有自己独立的地址空间,一个进程的崩溃不会影响另一个进程的执行. 线程:进程中的一个执行路径,一个进程中可以同时有多个线程在执行,当其中一个线程对公共资源做了修改,其他线程是可以看到的. 1.2.什么是并行和并发 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时. 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正

Java并发编程高级内容介绍

计数器:CountDownLatch CountDownLatch类似于一个计数器,和Atomic类比较相近,操作是原子的,即多个线程同时只能有一个可以去操作.CountDownLatch对象设置一个初始的数字作为计数值,任何调用这个对象上的await()方法都会阻塞,直到这个计数器的计数值被其他的线程调用countDown()减为0为止.典型的应用场景就是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行.例如在Zookeeper的使用过程中,由于客户端与服务器建立连

Java并发编程(三) 并发类库中的常用类

1. 同步容器类 遗留下来的同步容器类包括Vector和Hashtable,此外java.util.Collections类中还提供了以下工厂方法创建线程安全的容器对象: Collections.synchronizedList 返回支持同步操作(线程安全)的List对象: Collections.synchronizedSet 返回支持同步操作(线程全的)的Set对象: Collections.synchronizedMap 返回支持同步操作(线程安全)的Map对象: 需要注意的是,同步容器类

Java 并发编程(三):如何保证共享变量的可见性?

上一篇,我们谈了谈如何通过同步来保证共享变量的原子性(一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行),本篇我们来谈一谈如何保证共享变量的可见性(多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值). 我们使用同步的目的不仅是,不希望某个线程在使用对象状态时,另外一个线程在修改状态,这样容易造成混乱:我们还希望某个线程修改了对象状态后,其他线程能够看到修改后的状态——这就涉及到了一个新的名词:内存(可省略)可见性. 要了解可见性

<java并发编程的艺术>读书笔记-第三章java内存模型(一)

一概述 本文属于<java并发编程的艺术>读书笔记系列,继续第三章java内存模型. 二重排序 2.1数据依赖性 如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性.数据依赖分下列三种类型: 名称 代码示例 说明 写后读 a = 1;b = a; 写一个变量之后,再读这个位置. 写后写 a = 1;a = 2; 写一个变量之后,再写这个变量. 读后写 a = b;b = 1; 读一个变量之后,再写这个变量. 上面三种情况,只要重排序两个操作的执行顺序,

《java并发编程的艺术》读书笔记-第三章Java内存模型(二)

一概述 本文属于<java并发编程的艺术>读书笔记系列,第三章java内存模型第二部分. 二final的内存语义 final在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.可以参照之前整理的关键字final.这里作者主要介绍final域的内存语义. 对于final域,编译器和处理器要遵守两个重排序规则: 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序. 初次读一个包含final域的对象的引用,与随后初次读这

Java并发编程(三)volatile域

相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Android多线程(一)线程池 Android多线程(二)AsyncTask源代码分析 前言 有时仅仅为了读写一个或者两个实例域就使用同步的话,显得开销过大,volatile关键字为实例域的同步訪问提供了免锁的机制.假设声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被还有一个线程并发更新的. 再讲到volatile关键字之前我们须要了解一下内存模型的相关概念以及并发编程中的三个特性:原子性,可见