java并发之如何解决线程安全问题
并发(concurrency)一个并不陌生的词,简单来说,就是cpu在同一时刻执行多个任务。
而Java并发则由多线程实现的。
在jvm的世界里,线程就像不相干的平行空间,串行在虚拟机中。(当然这是比较笼统的说法,线程之间是可以交互的,他们也不一定是串行。)
多线程的存在就是压榨cpu,提高程序性能,还能减少一定的设计复杂度(用现实的时间思维设计程序)。
这么说来似乎线程就是传说中的银弹了,可事实告诉我们真正的银弹并不存在。
多线程会引出很多难以避免的问题, 如死锁,脏数据,线程管理的额外开销,等等。更大大增加了程序设计的复杂度。
但他的优点依旧不可替代。
死锁和脏数据就是典型的线程安全问题。
简单来说,线程安全就是: 在多线程环境中,能永远保证程序的正确性。
只有存在共享数据时才需要考虑线程安全问题。
java内存区域:

其中, 方法区和堆就是主要的线程共享区域。那么就是说共享对象只可能是类的属性域或静态域。
了解了线程安全问题的一些基本概念后, 我们就来说说如何解决线程安全问题。我们来从一个简单的servlet示例来分析:

public class ReqCounterServlet extends HttpServlet{
private int count = 0;
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
count++;
System.out.print("当前已达到的请求数为" + count);
}
public void doPost(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
// ignore
}
}

1. 了解业务场景的线程模型
这里的线程模型指的是: 在该业务场景下, 可能出现的线程调用实况。
众所周知,Servlet是被设计为单实例,在请求进入tomcat后,由Connector建立连接,再讲请求分发给内部线程池中的Processor,
此时Servlet就处于一个多线程环境。即如果存在几个请求同时访问某个servlet,就可能会有几个线程同时访问该servlet对象。如图:

线程模型,如果简单的话,就在脑海模拟一下就好了,复杂的话就可以用纸笔或其他工具画出来。
2. 找出共享对象
这里的共享对象就很明显就是ReqCounterServlet。
3. 分析共享对象的不变性条件
不变性条件,这个名词是在契约式编程的概念中的。不变性条件保证类的状态在任何功能被执行后都保持在一个可接受的状态。
这里可以引申出, 不可变对象是线程安全的。(因为不可变对象就没有不变性条件)
不变性条件则主要由对可变状态的修改与访问构成。
这里的servlet很简单, 不变性条件大致可以归纳为: 每次请求进入时count计数必须加一,且计数必须正确。
在复杂的业务中, 类的不变性条件往往很难考虑周全。设计的世界是险恶的,只能小心谨慎,用测量去证明,最大程度地减少错误出现的几率。
4. 用特定的策略解决线程安全问题。
如何解决的确是该流程的重点。目前分三种方式解决:
第一种,修改线程模型。即不在线程之间共享该状态变量。一般这个改动比较大,需要量力而行。
第二种,将对象变为不可变对象。有时候实现不了。
第三种,就比较通用了,在访问状态变量时使用同步。 synchronized和Lock都可以实现同步。简单点说,就是在你修改或访问可变状态时加锁,独占对象,让其他线程进不来。
这也算是一种线程隔离的办法。(这种方式也有不少缺点,比如说死锁,性能问题等等)
其实有一种更好的办法,就是设计线程安全类。《代码大全》就有提过,问题解决得越早,花费的代价就越小。
是的,在设计时,就考虑线程安全问题会容易的多。
首先考虑该类是否会存在于多线程环境。如果不是,则不考虑线程安全。
然后考虑该类是否能设计为不可变对象,或者事实不可变对象。如果是,则不考虑线程安全
最后,根据流程来设计线程安全类。
设计线程安全类流程:
1、找出构成对象状态的所有变量。
2、找出约束状态变量的不变性条件。
3、建立对象状态的并发访问管理策略。
有两种常用的并发访问管理策略:
1、java监视器模式。 一直使用某一对象的锁来保护某状态。
2、线程安全委托。 将类的线程安全性委托给某个或多个线程安全的状态变量。(注意多个时,这些变量必须是彼此独立,且不存在相关联的不变性条件。)
同步策略(文档化很重要):
定义了如何在不违背对象不变条件或后验条件的情况下对其状态的访问操作进行协同。同步策略规定了如何将不可变性,线程封闭,与加锁机制等结合起来以维护线程的安全性,并且还规定了哪些变量由哪些锁保护。
from: https://www.cnblogs.com/w2154/p/6637717.html
java并发之如何解决线程安全问题的更多相关文章
- java 22 - 12 多线程之解决线程安全问题的实现方式1
从上一章知道了多线程存在着线程安全问题,那么,如何解决线程安全问题呢? 导致出现问题的原因: A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 上一章的程序,上面那3条都具备, ...
- java线程安全问题以及使用synchronized解决线程安全问题的几种方式
一.线程安全问题 1.产生原因 我们使用java多线程的时候,最让我们头疼的莫过于多线程引起的线程安全问题,那么线程安全问题到底是如何产生的呢?究其本质,是因为多条线程操作同一数据的过程中,破坏了数据 ...
- Java之解决线程安全问题的方式三:Lock锁
import java.util.concurrent.locks.ReentrantLock; /** * 解决线程安全问题的方式三:Lock锁 --- JDK5.0新增 * * 1. 面试题:sy ...
- java 22 - 13 多线程之解决线程安全问题的实现方式2
上一章说了,解决线程安全问题的实现方式1是使用同步代码块 同时也知道了,同步代码块的锁对象是任意对象:(Object obj ; Demo d;)这些都行 那么,现在来说解决线程安全问题的实现方式2 ...
- Lock锁方式解决线程安全问题
在JDK5.0之后新增加了一种更强大的线程同步机制---通过显示定义同步锁来实现线程同步解决线程安全问题.同步锁使用Lock对象充当. java.util.concurrent.locks.lock接 ...
- 静态同步方法和解决线程安全问题_Lock锁
静态的同步方法锁对象是谁?不能是thisthis是创建对象之后产生的,静态方法优先于对象静态方法的锁对象是本类的cLass属性-->class文件对象(反射) 卖票案例出现了线程安全问题 卖出了 ...
- 解决线程安全问题_同步方法和解决线程安全问题_Lock锁
解决线程安全问题_同步方法 package com.yang.Test.ThreadStudy; import lombok.SneakyThrows; /** * 卖票案例出现了线程安全的问题 * ...
- java学习笔记 --- 多线程(线程安全问题——同步代码块)
1.导致出现安全问题的原因: A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 2.解决线程安全问题方法: 同步代码块: synchronized(对象){ 需要同步的代码; ...
- Java学习之多线程(线程安全问题及线程同步)
一.线程安全问题产生前提:1.多线程操作共享数据2.线程任务中有多条代码 class Ticket implements Runnable { //2.共享数据 private int num = 1 ...
随机推荐
- 转:Session,Token相关区别
参考地址:https://www.cnblogs.com/xiaozhang2014/p/7750200.html 1. 为什么要有session的出现?答:是由于网络中http协议造成的,因为htt ...
- mysql多线程插入速度与不同数据库之间的比较
package ThreadInsetMysql;import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQ ...
- 总结Selenium WebDriver中一些鼠标和键盘事件的使用
在使用 Selenium WebDriver 做自动化测试的时候,会经常模拟鼠标和键盘的一些行为.比如使用鼠标单击.双击.右击.拖拽等动作:或者键盘输入.快捷键使用.组合键使用等模拟键盘的操作.在 W ...
- SPLAY,LCT学习笔记(五)
这一篇重点探讨LCT的应用 例:bzoj 2631 tree2(国家集训队) LCT模板操作之一,利用SPLAY可以进行区间操作这一性质对维护懒惰标记,注意标记下传顺序和如何下传 #include & ...
- python接口自动化测试二十一:类和方法
# 类和方法 class Count(): def __init__(self, aaa, bbb): # 初始化 # 可以放公共的参数 print('实例化的时候,会执行init的内容') self ...
- Hibernate之关联关系映射(一对一主键映射和一对一外键映射)
1:Hibernate的关联关系映射的一对一外键映射: 1.1:第一首先引包,省略 1.2:第二创建实体类: 这里使用用户信息和身份证信息的关系,用户的主键编号既可以做身份证信息的主键又可以做身份证信 ...
- 表达式树ExpressionTrees
简介 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等.你可以对表达式树中的代码进行编辑和运算.这样能够动态修改可执行代码.在不同数据库 ...
- css盒子模型和定位
content padding border margin 可以理解为在商场上看到的电视机. 电视机------content 装电视机的箱子边框有粗细------border 电视机与箱子之间的泡沫 ...
- Mysql innodb_fast_shutdown
innodb_fast_shutdown有3个值: 默认是1 可选0 1 2 支持全动态局设置 使用场景:在做数据库关闭升级的时候 set global innodb_fast_shutdown=0 ...
- 第三章XML简介
概念:XML:提供数据交换.系统配置.内容管理等的功能,可跨平台.跨网络.跨程序的数据描述方式.XSL:依靠XPath定位,提供显示模板,且专门为了显示XML文件信息的语言.CSS(层叠样式表):在网 ...