java并发临界资源管理

所谓的并发,一般是指基于多处理器硬件环境的,允许系统在同一时刻执行多件不同的任务逻辑,在单处理器硬件环境下,一般是按照时间片轮转的调度方式,实现宏观意义上的并发,而事实上,在同一个时间点上,仍然只有一件任务在运行,我习惯把这种并发看成“伪并发”,以下所讲的并发临界资源管理,是基于多CPU硬件环境的。即在同一时刻,正在运行的不同CPU可能会访问一些共用的资源,而临界资源管理需要做的就是保证,这些资源的读写操作不能陷入死锁以及,各线程获得的资源都是最新的。
在提到并发之前,我得提到一个基础问题,那就是,程序在运行时,为了加快系统的运算速度,临时数据一般是放在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();//获取线程池
   for(int i=0;i<5;i++){
       es.execute(new Accessor(i));  //执行线程
   }
   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();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值