第二章:基础知识-线程安全性

       在构建稳定的并发程序师,必须正确的使用线程和锁。但这些终究只是一些机制。要编写线程安全的代码,其核心在于要对状态访问操作进行管理,特别是对共享的(shared)和可变的(mutable)状态访问。

       共享意味着变量可以有多个线程同时访问,而可变则意味着变量的值在其生命周期内可以发生变化。

       一个对象是否需要时线程安全的,取决于它是否被多个线程访问。当多个线程访问某个状态变量并且其中有一个线程执行写入操作室,必须采用同步机制来协同这些线程对变量的访问。

       java中主要同步机制是关键字 synchronized,它提供了一种独占的加锁方式,但“同步”这个术语还包括 volatile类型的变量,显式锁(Explicit Lock)以及院子变量。

        如果当多个线程访问同一个可变的状态变量时没有使用合适的同步,那么程序就会出现错误。有三种方式可以修复这个问题。

         1、不再线程之间共享该状态变量。

         2、将状态变量修改为不可变的变量。

         3、在访问状态变量时使用同步。

状态变量:个人理解为一个类的成员变量,或是可以由多个程序/线程调用的变量。

一、原子性

     例 ++count;

扫描二维码关注公众号,回复: 847206 查看本文章

    虽然这个递增操作是一种紧凑的语法,使其看上去只是一个操作,但这个操作并非是原型的,因而它并不会作为一个不可分割的操作来执行。实际上,他包含了三个独立的操作:读取count的值,将值+1,然后将结果写入count。这事一个“读取-修改-写入”的操作序列,并且其结果状态依赖于之前的状态。

1、竟态条件:当某个计算的正确性取决于多个线程的交替执行时序时,就会发生竟态条件。简单的说正确的结果取决于“运气”

要避免竟态条件,就必须在某个线程修改该变量时,通过某种方式防止其他线程使用这个变量,从而确保其他线程智能在修改操作完成之前活之后读取和修改状态,而不是在修改状态的过程中。

在java.util.concurrentl.atomic包中包含了ixie院子变量类,用于实现在数值和对象引用上的院子状态转换。

二、加锁机制

     1、内置锁:java提供了一种那只的锁机制来支持原子性:同步代码块(Synchronized Block)

以关键字synchronizated 来修饰的方法就是一种横跨整个方法提的同步代码块,其中台同步代码块的锁就是方法调用所在的对象。静态的synchronized方法是以Class对象作为锁的。

synchronizated(lock){
 //TODO
}

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

java的那只所相当于一种互斥体(或互斥锁),这意味着最多只有一个线程能持有这种所。当线程A尝试获取一个有B持有的锁时,A必须等待或者阻塞,知道B释放这个锁。由于每次之恩那个有一个线程执行内置锁保护的代码块,银子这个锁保护的同步代码块会议原子方式进行执行,多个线程在执行该代码块时也不会互相干扰。

 2、重入

重入意味着 获取所的操作的力度是线程而不是调用。

例:

public class Widget{

      public synchronized void doSomething(){

             ......
       }

}


public class LogginWidget extends Widget{

      public synchronized void doSomething(){

       System.out.println("calling doSomething");
      super.doSomething();
     }

}

 如果一个线程调用了 LoginWidget.doSomething();那么它会执行两个含有内置锁的方法 LoginWidget.doSomething()和Widget.doSomthing();如果锁是不可以重入的话,那么就永远也无法获得super.doSomething的锁。

个人对重入的理解:这两个内置锁为当前对象,当锁相同的时候就可以进行重入操作。

一种常见的加锁约定是,将所有的可以变状态都封装在对象内部,并通过对象的内置锁对所有访问可变状态的代码路经进型同步,使得在该对象上不会发生并发访问。

并非所有的数据都需要锁的保护,只有被多个线程同时访问的可变数据才需要通过锁来保护。

当执行时间较长的计算或者可能无法夸苏完成的操作时(例如:网络IO或者控制台IO)一定不要持有锁

猜你喜欢

转载自710173455.iteye.com/blog/2204143
今日推荐