java并发临界资源管理

所谓的并发,通常是指基于多处理器硬件环境的,容许系统在同一时刻执行多件不一样的任务逻辑,在单处理器硬件环境下,通常是按照时间片轮转的调度方式,实现宏观意义上的并发,而事实上,在同一个时间点上,仍然只有一件任务在运行,我习惯把这种并发当作“伪并发”,如下所讲的并发临界资源管理,是基于多CPU硬件环境的。即在同一时刻,正在运行的不一样CPU可能会访问一些共用的资源,而临界资源管理须要作的就是保证,这些资源的读写操做不能陷入死锁以及,各线程得到的资源都是最新的。  java

在提到并发以前,我得提到一个基础问题,那就是,程序在运行时,为了加快系统的运算速度,临时数据通常是放在CPU缓存(Cache)中的,而不一样的线程占有一段不一样的Cache,因此这里就涉及到一个问题,即对共享资源而言,如何保证全部线程读写的同步。这就是并发所要解决的临界资源管理问题的来由。java并发解决这个问题都是基于如下原理:保证每一个线程对共享资源的写操做都是独占式的,且将发生改变的共享资源写回到主存(memory)中,基于缓存一致性规则,系统全部Cache中的相同共享资源值将所有过时失效,其余线程的读操做被迫从主存中去取值了。(硬件实现,就不细说了) 编程

 一. Java多线程的实现  缓存

Java中从语言上实现多线程是经过实例化Thread类,调用实例start()方法,执行其run()方法所定义的任务来实现的。 获得咱们本身的Thread实例,通常有两种方式,一是让本身的任务类继承Thread父类,并覆写其run方法,最后实例化这个任务类,调用start()方法,启动线程。安全

 另外一种方法是任务类继承Runnable接口,并实现其run()方法,经过Thread myThread = new Thread(myRunnable),传入该Runnable实例做为构造器参数,从而得到Thread实例,调用start()方法,启动线程。这种方式较为经常使用。多线程

 以上提到的两种方式,都须要显示得到Thread类的实例,并针对每单个Thread进行操做与管理。但在所需构建的线程较多时,这种方式便显得较为繁琐,由于对系统资源的占用与释放问题都交给了咱们的设计者,因此Java中也提供了相似线程池的管理方式来管理这些线程。这些管理工具在java.util.concurrent包中。经过ExecutorService实例,进一步封装了Thread管理细节,经过调用其execute()方法,执行线程,并经过shutdown()方式,释放掉其中全部的线程所占用的资源。其通用代码以下:(推荐) 并发

 ExecutorService es = Executors.newCachedThreadPool();//获取线程池 dom

 for(int i=0;i<5;i++){ ide

       es.execute(new Accessor(i)); //执行线程 工具

 }this

 TimeUnit.SECONDS.sleep(5); 

 es.shutdown(); //释放全部资源 

其获取线程池的方式有三种,分别是 

Executors.newCachedThreadPool():建立的线程池大小等于实际线程数 Executors.newFixedThreadPool(int num):建立固定大小的线程池 Executors.newSingleThreadExecutor():固定线程池大小为1 

 二. 临界资源管理 

这部分涉及到几个经常使用的关键词,一个是synchronized,一个是volatile,一个是Atomatic*一系列原子类,以及ThreadLocal(线程本地存储)。 

 1. volatile 

这个关键字用来修饰变量,其功能和影响只须要记住一句话,它保证的是,被其所修饰的变量值,在每次发生改变以后,一定即时将改动后的值刷新写入主存中。 

其局限性也在于此,他只能保证变量的同步,而不是功能性的同步。例如对被volatile修饰的变量i,执行操做i++;这个操做并不线程安全,由于这个操做不是原子性的,它能够拆分红两步,一步是读i值,第二步是作加法;volatile只能保证第一步读到的值必定是当时最新的,但在第二步以前,该线程可能被暂时挂起,进而去执行其余的线程,若是其余线程在此时修改了i的值,那么第二步算出来的值就不是那么合理了。因此功能性的同步,就是synchronized这个关键字的事情了。 

 2. synchronized 

这是Java并发保证同步最经常使用到的一个关键字。利用该关键字来保证同步是经过加锁机制实现的,也能够说是一种隐式加锁方式,之因此这么说,是由于你可使用java.util.concurrent.locks类库中提供的Lock类显示地对代码块进行加锁以实现线程同步。

 Java中的每个对象均可以做为锁,这里的锁是针对加锁和解锁两种操做来讲的,即同一时刻至多只有一个线程可以访问做为锁的对象。根据synchronized的用途:synchronized能够用于修饰普通方法、静态方法以及代码块,锁的表现形式有如下三种: 

对于普通同步方法,锁是当前实例对象;对于静态同步方法,锁是当前类的Class对象;对于同步方法块,锁是synchronized括号里参数指明的对象。 

关于synchronized须要说明的有两点: 

(1)对于某个特定对象来讲,其全部synchronized方法共享同一个锁,即当一个类中同时包含多个Synchronized修饰的成员方法时,该类的一个实例对象,同一时刻只能访问其中一个成员方法,对同步代码块,这一规则一样成立。 

(2)synchronized关键字不属于方法特征签名的组成部分,因此能够在覆盖方法的时候加上去。 

 3. 原子类 

须要首先说明的是,原子操做是指不能被线程调度机制中断的操做。原子性能够用于除了long和double以外的全部基本类型之上的简单操做,即对于读取和写入除long和double以外的基本类型变量的值的操做,是原子操做。可是因为long和double为64bit数据,而JVM对64位的读取和写入是当作两个分离的32位操做来执行,这就可能产生一个在读取和写入操做中间发生上下文切换的隐患,致使不正确结果的可能性,这也被称为字撕裂。因此Java SE5引入了诸如AtomicInteger、AtomicLong以及AtomicReference等特殊的原子性变量,并提供了其对应的读写方法,从而保证其读写操做的原子性。具体参考手册,其类库为java.util.concurrent.atomic。 

 4. ThreadLocal 

引用自《Java编程编程思想》——“防止任务在共享资源上产生冲突的第二种方式是根除对变量的共享。线程本地存储是一种自动化机制,能够为使用相同变量的每一个不一样的线程都建立不一样的存储。所以,若是你有5个线程都要使用变量X所表示的对象,那么线程本地存储会生成5个用于X的不一样的存储块。”,而建立和管理线程本地存储能够用java.lang.ThreadLocal类来实现。 

 其提供的示例代码以下: 

 /** 

 * Created by Song on 2016/10/15. 

 */

 public class ThreadLocalVariableHolder {

      private static ThreadLocal<Integer> values = new ThreadLocal<Integer>(){ 

        private Random rand = new Random(47); 

       @Override 

       protected synchronized Integer initialValue() { 

              return rand.nextInt(10000); 

        } 

 }; 

 public static void increment(){ 

 values.set(values.get()+1); 

 } 

 public static int get(){return values.get();} 

 public static void main(String [] args) throws InterruptedException{ 

        ExecutorService es = Executors.newSingleThreadExecutor(); 

        for(int i=0;i<5;i++){ 

              es.execute(new Accessor(i)); 

        } 

        TimeUnit.SECONDS.sleep(5); 

        es.shutdown(); 

    } 

}

 class Accessor implements Runnable{ 

      private final int id; 

      public Accessor(int id){this.id=id;} 

      public void run() { 

          while (!Thread.currentThread().isInterrupted()){                     ThreadLocalVariableHolder.increment(); 

System.out.println(this); Thread.yield(); 

    } 

 } 

 public String toString(){ 

       return "#"+id+": "+ThreadLocalVariableHolder.get();

   }

 } 

相关文章
相关标签/搜索