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

原文地址:https://www.cnblogs.com/GarfieldEr007/p/10230905.html

时间: 2024-10-13 16:01:36

java并发之如何解决线程安全问题的相关文章

java 22 - 12 多线程之解决线程安全问题的实现方式1

从上一章知道了多线程存在着线程安全问题,那么,如何解决线程安全问题呢? 导致出现问题的原因: A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 上一章的程序,上面那3条都具备,所以肯定出问题. 如何解决问题: 原因A.B肯定不能改变,所以只能改变原因C 解决问题思路: 如果把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行这个整体的时候,别的线程不能执行. 这时候就用到了java提供的:同步机制 同步代码块: synchronized(对象){  需要同步的代码

Spring学习11- Spring使用ThreadLocal解决线程安全问题

ThreadLocal是什么      早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程程序. ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”.其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些.      当使用ThreadLoca

如何解决线程安全问题

http://www.cnblogs.com/dolphin0520/p/3923737.html 一.什么时候会出现线程安全问题? 在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的的资源:一个变量.一个对象.一个文件.一个数据库表等,而当多个线程同时访问同一个资源的时候,就会存在一个问题: 由于每个线程执行的过程是不可控的,所以很可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错. 举个简单的例子: 现在有两个线程分别从网

解决线程安全问题

线程带来的风险 线程安全性问题 出现安全性问题的需要满足的条件:多线程环境.有共享资源.非原子性操作 活跃性问题 死锁 饥饿 活锁 性能问题 cpu上下文切换会有性能问题(cpu分时间片执行) 锁 自旋锁 自旋其实就是当一个线程获取到锁之后,其他的线程会进行阻塞等待,一直到这个线程释放锁后才能进入 重入锁 & 锁重入 锁重入即在一个对象中对两个方法都加锁了,那么在一个线程获取到其中一个方法的锁后,再执行另外一个方法时就不再需要获取锁了:同时如果一个线程获取到了其中一个方法的锁,那么其他的线程既不

Java中如何解决线程安全问题

给出一个问题,如下: 解决方案如下: 1 public class Demo_5 { 2 3 public static void main(String[] args) { 4 //创建一个窗口 5 TicketWindow tw1=new TicketWindow(); 6 7 //使用三个线程同时启动 8 Thread t1=new Thread(tw1); 9 Thread t2=new Thread(tw1); 10 Thread t3=new Thread(tw1); 11 12 t

JAVA多线程(一)线程安全问题产生的原因

JAVA线程内存与主存间映射示意图 Java内存模型中规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程使用的变量到主内存副本拷贝,线程对变量的所有操作(读取.赋值)都必须在工作内存中进行,而不能直接读写主内存中的变量.不同线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要在主内存来完成. 工作内存与主内存间交互操作 Java内存模型只保证操作必须按顺序执行,而没有保证必须是连续执行. 以下八种操作来完成 lock(锁定):作用于主内存的变

阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第3节 线程同步机制_4_解决线程安全问题_同步代码块

同步代码块 解决了线程安全的问题. 原文地址:https://www.cnblogs.com/wangjunwei/p/11260470.html

浅谈利用同步机制解决Java中的线程安全问题

我们知道大多数程序都不会是单线程程序,单线程程序的功能非常有限,我们假设一下所有的程序都是单线程程序,那么会带来怎样的结果呢?假如淘宝是单线程程序,一直都只能一个一个用户去访问,你要在网上买东西还得等着前面千百万人挑选购买,最后心仪的商品下架或者售空......假如饿了吗是单线程程序,那么一个用户得等前面全国千万个用户点完之后才能进行点餐,那饿了吗就该倒闭了不是吗?以上两个简单的例子,就说明一个程序能进行多线程并发访问的重要性,今天就让我们去了解一下Java中多线程并发访问这个方向吧. **第一

java设计模式--解决单例设计模式中懒汉式线程安全问题

首先写个单例,懒汉模式: public class SingleDemo { private static SingleDemo s = null; private SingleDemo(){} public static SingleDemo getInstance(){ if(s == null){ s = new SingleDemo(); } return s; } } 写个测试类: public class ThreadDemo3 { public static void main(S