MOYUN(/Java/SQL/Linux/DevOps/运维/架构/管理/敏捷/开发)

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

并发(concurrency)一个并不陌生的词,简单来说,就是cpu在同一时刻执行多个任务。

而Java并发则由多线程实现的。

在jvm的世界里,线程就像不相干的平行空间,串行在虚拟机中。(当然这是比较笼统的说法,线程之间是可以交互的,他们也不一定是串行。)

多线程的存在就是压榨cpu,提高程序性能,还能减少一定的设计复杂度(用现实的时间思维设计程序)。

这么说来似乎线程就是传说中的银弹了,可事实告诉我们真正的银弹并不存在

死锁脏数据就是典型的线程安全问题。

简单来说,线程安全就是: 在多线程环境中,能永远保证程序的正确性

只有存在共享数据时才需要考虑线程安全问题

其中, 方法区就是主要的线程共享区域。那么就是说共享对象只可能是类的属性域或静态域

那么,什么才是线程安全的呢?

如果你在Google搜索就会出现许多像这样的“定义”:

  1. 线程安全的代码是多个线程同时执行也能工作的代码
  2. 如果一段代码可以保证多个线程访问的时候正确操作共享数据,那么它是线程安全的

你有没有觉得上面的定义似乎没有传达任何有意义的信息,反而会让你更混乱。 虽然这些定义都是没错的,但事实是没有提供任何实际的帮助和观点,

我们如何区分线程安全类和不安全类呢?线程 “安全” 是什么意思?


线程安全任何合理定义的核心都是在讲线程安全的正确性。因此,在了解线程安全之前,我们首先应该了解这个 “正确性”

正确性意味着类的行为符合规范

你当然认可这个定义,一个好的类规范会在任何时间都可以获得类的状态信息,并且可以对它进行操作。 通常我们没有一个准则来定义自己的类是否符合规范,怎么知道它们是正确的? 当然不知道,但我们依然可以使用它,一旦我们能够让自己的 “代码有效”。 这种 “代码自信” 与我们接近正确性有关

只要把“正确性”定义为我们可以理解的内容,现在我们可以用一种非专业的解释来定义它:当一个类被多个线程进行访问并且正确运行,它就是线程安全的

当多个线程访问某各类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。

 

示例:无状态的Servlet

一个很好的线程安全的例子是没有字段和引用的 Servlet 类,因为没有字段和引用所以是 无状态 的

public class StatelessFactorizer implements Servlet{

    public void service(ServletRequest req, ServletResponse resp) {
        BigInteger i = extractFromRequest(req);
        BigInteger[] factors = factor(i);
        encodeIntoResponse(resp, factors);
    }
}

特定计算的临时状态被存储在线程堆栈的局部变量表中,只能被当前执行的线程访问。 线程A访问 StatelessFactorizer 不会影响线程B访问同一个 StatelessFactorizer; 因为两个线程不共享状态,就像是在访问不同的实例。 由于访问无状态的对象时线程的操作不会影响其他线程中操作的正确性,因此无状态对象是线程安全的

多线程假想:众所周知,Servlet是被设计为单实例,在请求进入tomcat后,由Connector建立连接,再讲请求分发给内部线程池中的Processor,
此时Servlet就处于一个多线程环境。即如果存在几个请求同时访问某个servlet,就可能会有几个线程同时访问该servlet对象。

多线程模型下的并发访问策略

1、java监视器模式。  一直使用某一对象的锁来保护某状态。

2、线程安全委托。  将类的线程安全性委托给某个或多个线程安全的状态变量(注意多个时,这些变量必须是彼此独立,且不存在相关联的不变性条件)

 

解决机制

1、加锁:

  a、锁能使其保护的代码以串行的形式来访问,当给一个复合操作加锁后,能使其成为原子操作。一种错误的思想是只要对写数据的方法加锁,其实这是错的,对数据进行操作的所有方法都需加锁,不管是读还是写

  b、加锁时需要考虑性能问题,不能总是一味地给整个方法加锁synchronized就了事了,应该将方法中不影响共享状态且执行时间比较长的代码分离出去

  c、加锁的含义不仅仅局限于互斥,还包括可见性。为了确保所有线程都能看见最新值,读操作和写操作必须使用同样的锁对象

2、不共享状态:

  无状态对象: 无状态对象一定是线程安全的,因为不会影响到其他线程

  线程关闭: 仅在单线程环境下使用

3、不可变对象:

  可以使用final修饰的对象保证线程安全,由于final修饰的引用型变量(除String外)不可变是指引用不可变,但其指向的对象是可变的,所以此类必须安全发布,也即不能对外提供可以修改final对象的接口

posted on 2018-04-22 16:59  moyun-  阅读(15167)  评论(1编辑  收藏  举报