Java并发编程入门(二)

1.竞态条件

1.1 定义

当某个计算的正确性取决于多个线程的交替执行时序时,就会发生竞态条件。换句话说,正确的结果要取决于运气。

最常见的竞态条件类型:先检查后执行(Check-Then-Act)操作,即通过一个可能失效的观测结果来决定下一步的动作。

1.2 特征

与大多数并发错误一样,竞态条件并不总是会产生错误,还需要某种不恰当的执行时序。

1.3 复合操作

为了确保线程安全性,“先检查后执行”(例如延迟初始化)和“读取-修改-写入”等操作统称为复合操作:包含了一组必须以原子方式执行的操作以确保线程安全性。

 

2 加锁机制

2.1

在线程安全性定义中要求,多个线程之间的操作无论采用合种执行时序或交替方式,都要保证不变性条件不被破坏。当在不变性条件中涉及多个变量时,各个变量之间并不是彼此独立的,而是某个变量的值会对其他变量的值产生约束。因此,当更新某一个变量时,需要在同一个原子操作中对其他变量同时进行更新。

2.2 内置锁

Java提供了一种内置的锁机制来支持原子性:同步代码块(Synchronized Block)。同步代码块包括两部分:一个作为锁的对象引用,一个作为由这个锁保护的代码块。

每个Java对象都可以用作一个实现同步的锁,这些锁被称为内置锁(Intrinsic Lock)或监视器锁(Monitor Lock)。线程在进入同步代码块之前会自动获得锁,并且在退出同步代码块时自动释放锁,而无论是通过正常的控制路径退出,还是通过从代码块中抛出异常退出。获得内置锁的唯一途径就是进入由这个锁保护的同步代码块或方法。

Java的内置锁相当于一种互斥体(或互斥锁),这意味着最多只有一个线程能持有这种锁。

当线程A尝试获取一个由线程B持有的锁时,线程A必须等待或阻塞,直到线程B释放这个锁。如果B永远不释放锁,那么A也将永远地等待下去。

 

2.3 锁的重入

如果某个线程试图获得一个已经由它自己持有的锁,那么请求就会成功。

重入进一步提升了加锁行为的封装性,因此简化了面向对象并发代码的开发。

2.4 用锁来保护状态

由于锁能使其保护的代码路径以串行形式来访问,因此可以通过锁来构造一些协议以实现对共享状态的独占访问。只要始终遵循这些协议,就能确保状态的一致性。

2.5

不良并发(Poor Concurrency)应用程序:可同时调用的数量,不仅受到可用处理资源的限制,还受到应用程序本身结构的限制:任意一个请求均需要等待前一个请求执行完成。

确保并发的同时维护线程安全性:缩小同步代码块的范围,并尽量避免将IO等耗时操作加在同步代码块中。

原文地址:https://www.cnblogs.com/ygria/p/9388240.html

时间: 2024-12-11 09:38:24

Java并发编程入门(二)的相关文章

Java并发编程入门与高并发面试

第1章 课程准备(入门课程)课程目标:Java并发编程入门,适合没有并发编程经验的同学,本章首先从课程重点.特点.适合人群及学习收获几个方面对课程进行整体的介绍,然后会从一个实际的计数场景实现开始,给大家展示多线程并发时的线程不安全问题,让大家能够初体验到并发编程,之后会讲解并发和高并发的概念,并通过对比让大家明白到底什么是并发和...1-1 课前必读(不看会错过一个亿)1-2 课程导学1-3 并发编程初体验1-4 并发与高并发基本概念(选看)1-5 JAVA内存模型1-6 并发的优势与风险(选

Java 并发编程(二)对象的不变性和安全的发布对象

一.不变性 满足同步需求的另一种方法是使用不可变对象(Immutable Object).到目前为止,我们介绍了许多与原子性和可见性相关的问题,例如得到失效数据,丢失更新操作或光查到某个对象处于不一致的状态等等,都与多线程视图同时访问同一个可变的状态相关.如果对象的状态不会改变,那么这些问题与复杂性也就自然消失了. 如果某个对象在被创建后其状态就不能被修改,那么这个对象就被成为不可变对象.线程安全型是不可变对象的固有属性之一,他们的不变性条件是由构造函数创建的,只要他们的状态不改变,那么这些不变

Java 并发编程(二)对象的不变性和安全的公布对象

一.不变性 满足同步需求的还有一种方法是使用不可变对象(Immutable Object). 到眼下为止,我们介绍了很多与原子性和可见性相关的问题,比如得到失效数据.丢失更新操作或光查到某个对象处于不一致的状态等等,都与多线程视图同一时候訪问同一个可变的状态相关.假设对象的状态不会改变,那么这些问题与复杂性也就自然消失了. 假设某个对象在被创建后其状态就不能被改动,那么这个对象就被成为不可变对象.线程安全型是不可变对象的固有属性之中的一个,他们的不变性条件是由构造函数创建的,仅仅要他们的状态不改

Java并发编程入门(一)

一.为什么要并发? 出现背景:操作系统的出现,使计算机同时运行多个程序成为可能. 1.目的: 资源利用率.某些时候,程序必须等待一些外部操作完成(IO)才能继续运行,在等待时间运行其他程序,可以有效提高资源利用率. 公平性.不同的用户和程序对计算机的资源有公平的利用率. 便利性.为了完成一个任务,同时运行多个计算机程序并进行通信,比只运行一个计算机程序更方便. 2.线程 线程也被称为轻量级进程,如果没有调度机制,线程将独立运行.同一进程中的所有线程共享进程的地址空间,实现了更细粒度的资源共享机制

JAVA并发编程(二)

设计线程安全的类 设计线程安全类的过程中需要注意三个基本要素: 1.找出构成对象的所有变量 2.找出约束状态变量的不变性条件 3.建立对象状态的并发访问管理策略 同步策略定义了如何在不违背对象不变性条件或者后验条件的情况下对其状态的访问操作进行协同.同步策略规定了如何将不变性.线程封闭和加锁机制等结合起来以维护线程的安全性,并且规定了哪些变量由哪些锁来保护.确保可以对这个类进行分析维护,就需要将同步策略写为正式文档. 如果不了解对象的不变性体条件,就不能确保线程安全性.要满足在状态变量的有效值或

Java 并发编程(二)对象的可见性

要编写正确的并发程序,关键问题在于:在访问共享的可变状态时需要进行正确的管理. 在第一部分,我们介绍了如果通过同步来避免多个线程在同一时刻访问相同的数据,而这节,我们将介绍如何共享和发布对象,从而使他们能够安全的由多个线程同时访问.这两部分形成了构建线程安全类以及通过 java.util.concurrent 类库来构建并发应用程序的重要基础. 我们已经知道了同步代码块和同步方法可以确保以原子的方式执行操作,但一种常见的误解是,认为关键字 synchronized 只能用于实现原子性或者确定"临

Java并发编程(二)为什么需要多线程

如果不考虑多线程的话,那么在程序只有一条执行路径,代码串行执行:顺序执行.选择或者循环.单线程就像你用你惯常的手去写字,多线程编程就要求你左手画圆,右手画方.一不留神就会手忙脚乱,圆不是圆,方也不像方.在继续学习多线程编程之前,先来小小的回顾一下操作系统的进化史. 手工操作:计算机并不包含操作系统,从头到尾只执行一个程序,这个程序可以访问程序中所有的资源. 批处理系统:加载在计算机上的一个系统软件,在它的控制下,计算机能够自动地.成批地处理一个或多个用户的作业(这作业包括程序.数据和命令). 多

Java并发编程(二)线程任务的中断(interrupt)

使用interrupt()中断线程 当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它,该方法只是在目标线程中设置一个标志,表示它已经被中断,并立即返回. package com.csdhsm.concurrent; import java.util.concurrent.TimeUnit; /** * @Title: InterruptDemo.java * @Package: com.csdhsm.concurrent * @Description

Java 并发编程(二)对象的发布逸出和线程封闭

对象的发布与逸出 "发布(Publish)"一个对象是指使对象能够在当前作用域之外的代码中使用.可以通过 公有静态变量,非私有方法,构造方法内隐含引用 三种方式. 如果对象构造完成之前就发布该对象,就会破坏线程安全性.当某个不应该发布的对象被发布时,这种情况就被称为逸出(Escape). 下面我们首先来看看一个对象是如何逸出的. 发布对象最简单的方法便是将对象的引用保存到一个共有的静态变量中,以便任何类和线程都能看见对象,如下面代码. public static Set<Stri