Java并发编程实战 第2章 线程安全性
编写线程安全的 代码,核心在与对共享的和可变的对象的状态的访问。
如果多个线程访问一个可变的对象时没有使用同步,那么就会出现错误。在这种情况下,有3中方式可以修复这个问题:
- 不在线程之间共享该状态变量
- 将状态变量修改为不可变的变量
- 在访问状态变量时使用同步
线程安全性的定义:
在多个线程访问某个类时,不管运行环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么我们就说这个类是线程安全的。
无状态对象:
不包含任何域,也不包含对其他类中域的引用。
一个无状态的对象,一定是线程安全的。
比如我们平时写的Servlet。
PS:Servlet中可能包含一个Dao域,但是在和Dao本身也是无状态的,我们本身是可以将Dao的代码直接分解到Servlet中,不需要Dao。但是为了实现代码解耦的效果,加入了Dao。
竞态条件
竞态条件(race condition),从多进程间通信的角度来讲,是指两个或多个进程对共享的数据进行读或写的操作时,最终的结果取决于这些进程的执行顺序。
常见的竞态条件如:先检查后执行。
原子操作:
一些简单的竞态条件,可以通过原子操作,解决。
如通过AtomicInteger的incrementAndGet()解决计数器的竞态条件
加锁机制:
复杂的竞态条件需要使用锁来解决。
每个java对象都有一个内置的锁,这个锁被称为内置锁或者监视锁。在这个对象内部的方法上(非static方法)的使用synchronized就是默认使用的这个锁。
内置锁是互斥的,可重入的。
用锁来保护状态
对于可能被多个线程同时访问的可变状态变量,在访问它时都需要持有同一个锁,这种状态下,我们成状态变量时由这个锁保护的。
活跃性和性能
一些概念:
安全性是指"永远不会发生糟糕的事情",我们使用锁和同步就是为了保证安全性。
活跃性是指"某件正确的事情最终会发生",当某个操作不能执行下去,就会出现活跃性问题,如死循环。
性能问题:响应时间,吞吐率,资源消耗,伸缩性等。
我们可以通过锁来解决安全问题,但是也要兼顾活跃性和性能。
重要的是不要粗暴的直接在一个复杂的方法上加synchronize,而是分解。我们看一个例子:
- package com.zjf;
- import java.math.BigInteger;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.TimeUnit;
- /**
- * 一个获取用于计算整数的平方的类
- * 实现了缓存
- * @author hadoop
- *
- */
- public class PowerCache {
- //使用全局变量 作为竞争资源
- public static final PowerCache pc = new PowerCache();
- //记录上一个传入的数值 为多个线程共享
- private BigInteger lastNumber;
- //记录上一个计算的平方结果 为多个线程共享
- private BigInteger lastPower;
- //记录power方法调用了多少次 为多个线程共享
- private long hits;
- //记录命中缓存的次数 为多个线程共享
- private long cacheHits;
- //访问了共享数据 注意标注方法为synchronized
- public synchronized long getHits()
- {
- return hits;
- }
- //访问了共享数据 注意标注方法为synchronized
- public synchronized long getCacheHit()
- {
- return cacheHits;
- }
- //命中缓存的概率 访问了共享数据 注意标注方法为synchronized
- public synchronized double getCacheHitRatio()
- {
- return (double)cacheHits / (double) hits;
- }
- public BigInteger power(BigInteger i)
- {
- BigInteger power = null;
- //分割对比和命中缓存部分为一个synchronized
- synchronized(this) {
- hits++;
- if(i.equals(lastNumber))
- {
- power = lastPower;
- cacheHits++;
- }
- }
- if(power == null)
- {
- //将计算这种耗时逻辑放在synchronized外部
- power = i.pow(2);
- //分割的第二个synchronized块 用于没有命中的结果 需要记录进缓存
- synchronized (this) {
- lastNumber = i;
- lastPower = new BigInteger(power.toString());//复制一份 这一步骤有问题 其实不用复制 这是多此一举 因为BigInteger是不可变对象
- }
- }
- return power;
- }
- //测试逻辑
- public static void main(String[] args) throws InterruptedException {
- PowerCache pc = new PowerCache();
- ExecutorService es = Executors.newCachedThreadPool();
- for(int i = 0; i < 100; i++)
- {
- es.execute(new Runnable() {
- public void run() {
- BigInteger bi = BigInteger.valueOf((long)(Math.random() * 10));
- System.out.println(bi + ":" + PowerCache.pc.power(bi));
- }
- });
- }
- es.shutdown();
- TimeUnit.MILLISECONDS.sleep(1000);
- System.out.println(PowerCache.pc.getHits());
- System.out.println(PowerCache.pc.getCacheHit());
- System.out.println(PowerCache.pc.getCacheHitRatio());
- }
- }
要判断同步代码块的合理大小,需要在各种设计之间进行权衡,包括安全性,简单性,和性能。
当执行时间较长的计算或者无法快速完成的操作时,如网络IO或者控制台IO。一定不要持有锁。
Java并发编程实战 第2章 线程安全性的更多相关文章
- java并发编程实战:第二章----线程安全性
一个对象是否需要是线程安全的取决于它是否被多个线程访问. 当多个线程访问同一个可变状态量时如果没有使用正确的同步规则,就有可能出错.解决办法: 不在线程之间共享该变量 将状态变量修改为不可变的 在访问 ...
- 《Java并发编程实战》第二章 线程安全性 读书笔记
一.什么是线程安全性 编写线程安全的代码 核心在于要对状态訪问操作进行管理. 共享,可变的状态的訪问 - 前者表示多个线程訪问, 后者声明周期内发生改变. 线程安全性 核心概念是正确性.某个类的行为与 ...
- Java并发编程实战 第8章 线程池的使用
合理的控制线程池的大小: 下面内容来自网络.不过跟作者说的一致.不想自己敲了.留个记录. 要想合理的配置线程池的大小,首先得分析任务的特性,可以从以下几个角度分析: 任务的性质:CPU密集型任务.IO ...
- 《Java并发编程实战》第二章 线程安全 札记
一个.什么是线程安全 编写线程安全的代码 其核心是管理国事访问的操作. 共享,可变的状态的訪问 - 前者表示多个线程訪问, 后者声明周期内发生改变. 线程安全性 核心概念是正确性.某个类的行为与其规范 ...
- 读书笔记-----Java并发编程实战(一)线程安全性
线程安全类:在线程安全类中封装了必要的同步机制,客户端无须进一步采取同步措施 示例:一个无状态的Servlet @ThreadSafe public class StatelessFactorizer ...
- java并发编程笔记(三)——线程安全性
java并发编程笔记(三)--线程安全性 线程安全性: 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现 ...
- Java并发编程实战---第六章:任务执行
废话开篇 今天开始学习Java并发编程实战,很多大牛都推荐,所以为了能在并发编程的道路上留下点书本上的知识,所以也就有了这篇博文.今天主要学习的是任务执行章节,主要讲了任务执行定义.Executor. ...
- 《Java并发编程实战》学习笔记 线程安全、共享对象和组合对象
Java Concurrency in Practice,一本完美的Java并发参考手册. 查看豆瓣读书 推荐:InfoQ迷你书<Java并发编程的艺术> 第一章 介绍 线程的优势:充分利 ...
- Java并发编程实战 第16章 Java内存模型
什么是内存模型 JMM(Java内存模型)规定了JVM必须遵循一组最小保证,这组保证规定了对变量的写入操作在何时将对其他线程可见. JMM为程序中所有的操作定义了一个偏序关系,称为Happens-Be ...
随机推荐
- apache配置静态缓存
配置静态缓存:节省带宽,加快访问速度,提高用户体验.<IfModule mod_expires.c> ExpiresActive on ExpiresByType image/gif &q ...
- 阶段3 3.SpringMVC·_01.SpringMVC概述及入门案例_03.入门程序之需求分析
- selenium之css selector定位
什么是CSS Selector? Css Selector定位实际就是HTML的Css选择器的标签定位 工具 Css Selector的练习建议大家安装火狐浏览器(49及以下版本)后,下载插件Fire ...
- 使用var提升变量声明
使用var 定义变量还会提升变量声明,即使用var定义:function hh(){ console.log(a); var a = 'hello world';}hh() //undefined 不 ...
- js获取当天时间,7天前后时间,时间格式化
格式化时间年月日时分秒 //时间戳转换方法 date:时间戳数字 formatDate(date) { var date = new Date(date); var YY = date.getFull ...
- python学习之模块-模块(二)
5.2 序列化模块 将一种数据结构转换成一种特殊的序列(字符串或bytes)的过程就叫序列化.这个特殊的序列还可以通过命令反解回原来的数据类型. python中有三种序列化的功能模块: json模 ...
- python目录和引用关系
这是我的项目目录 像这样引用没有直接画横线 但是运行时会报错:找不到 typeidea.typeidea.文件路径 图片拖出来看更清晰 后期补充: 解决方案: 如:右击:typeidea----- ...
- 【PyTorch】计算局部相似矩阵
计算局部相似矩阵 代码文档:https://github.com/lartpang/mypython/blob/master/2019-09-25%E8%AE%A1%E7%AE%97%E5%B1%80 ...
- 【VS开发】模态对话框和非模态对话框
MFC中对话框有两种形式,一个是模态对话框(model dialog box),一个是非模态对话框(modeless dialog box). 一.模态对话框(model dialog box) 在程 ...
- 自带的simple认证
参考: hdfs权限: 官网http://hadoop.apache.org/docs/r1.0.4/cn/hdfs_permissions_guide.html hdfs权限: http://dyi ...