驯服共享可变性

如果一个变量可以给多个线程读写,则我们说这个变量是共享的。下面通过一个栗子一步一步认识共享变量。

这是一个关于电源的控制程序,可以充电,用电,取得当前电量,停止充电。

package com.winter.Concurrency.April.TeachSample;

/**
 * 这个程序的问题
 * 1.level是一个非安全的共享可变变量
 * 2.replenish函数在大多数情况是睡眠状态,但是它要消耗一个线程,如果线程过多会溢出
 * 3.在构造函数中调用Thread的start方法,Thread类会自动插入一个内存栅栏,并将尚未构架完成的对象暴露给其他线程。
 * 4.EnergySource的实现破坏了类不变式。
 */
public class EnergySource {

    private final long MAXLEVEL=100;
    private long level=MAXLEVEL;
    private boolean keepRunning =true;
    //构造器初始化线程
    public EnergySource(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                replenish();
            }
        }).start();
    }
    //充电函数
    public void replenish(){
        while(keepRunning){
            if(level<MAXLEVEL){
                level++;
            }
            try { 
                Thread.sleep(1000);
            }catch (InterruptedException ex){
                System.out.println(ex.getMessage());
            }
        }
    }
    //取得当前电量
    public long getUnitLevelAvailable(){
        return level;
    }
    //停止充电
    public void StopEnergySource(){
        keepRunning=false;
    }
    //使用电量
    public boolean useEnergy(final long unit){
        if(unit>0&&unit<=level){
            level-=level;
            return true;
        }
        return false;
    }
}

上面的程序存在很多问题

package com.winter.Concurrency.April.TeachSample;

/**
 *为了不破坏类的不变式,考虑使用静态工厂函数来代替构造函数。即在一个静态工厂函数中创建对象实例,并将实例的引用返回给调用方之前启动的线程。
 *这种方法可以保证后台的任务能够在对象创建完成之后启动,从而保证了对象在创建和初始化的过程中一直处于有效状态。
 *
 */
public class FixEnergySource1 {

    private final long MAXLEVEL=100;
    private long level=MAXLEVEL;
    private boolean keepRunning =true;

    //静态工厂方法
    public static FixEnergySource1 create(){
        final FixEnergySource1 fixEnergySource1=new FixEnergySource1();
        fixEnergySource1.init();
        return fixEnergySource1;
    }
    //初始化线程的方法
    private void init(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                replenish();
            }
        }).start();
    }
    //充电函数
    public void replenish(){
        while(keepRunning){
            if(level<MAXLEVEL){
                level++;
            }
            try {
                Thread.sleep(1000);
            }catch (InterruptedException ex){
                System.out.println(ex.getMessage());
            }
        }
    }
    //取得当前的电量
    public long getUnitLevelAvailable(){
        return level;
    }
    //停止充电
    public void StopEnergySource(){
        keepRunning=false;
    }
    //使用电量
    public boolean useEnergy(final long unit){
        if(unit>0&&unit<=level){
            level-=level;
            return true;
        }
        return false;
    }
}
package com.winter.Concurrency.April.TeachSample;

import java.util.concurrent.*;

/**
 * 由于线程资源非常有限,所以创建的行为就不能太随意,选用java.util.Timer周期性的运行。
 * 在创建大量的FixEnergySource2的实例的时候,能实现线程复用。取消keepRunning属性,直接停掉周期任务。
 *
 *
 */
public class FixEnergySource2 {

    private final long MAXLEVEL=100;
    private long level=MAXLEVEL;
    //因为任务很少,所有10个线程就够了。
    private static final ScheduledExecutorService replenishTimer= Executors.newScheduledThreadPool(10);
    //上面的replenishTimer声明为static可以帮助我们在ScheduledThreadPool中共享线程池里的线程,我们必须想出一个将replenishTimer
    //关闭的方法,默认情况下,执行线程都是非守护线程,如果我们不手动将它关闭,jvm是不会替我们关闭的,当然我们可以手动调用replenishTimer
    //.shutdown();方法进行关闭,也可以使用下面的线程工厂,保证所产生的线程都是守护线程。
//    private static final ScheduledExecutorService replenishTimer= Executors.newScheduledThreadPool(10, new ThreadFactory() {
//        @Override
//        public Thread newThread(Runnable r) {
//            Thread thread=new Thread(r);
//            thread.setDaemon(true);
//            return thread;
//        }
//    });

    private ScheduledFuture<?> replenishTask;
    private boolean keepRunning =true;
    //默认构造器私有化
    private FixEnergySource2(){

    }

    public static FixEnergySource2 create(){
        final FixEnergySource2 fixEnergySource2=new FixEnergySource2();
        fixEnergySource2.init();
        return fixEnergySource2;
    }
    //初始化周期充电的任务
    private void init(){
       replenishTask=replenishTimer.scheduleAtFixedRate(new Runnable() {
           @Override
           public void run() {
               replenish();
           }
       },0,1, TimeUnit.SECONDS);
    }
    //充电的函数
    public void replenish(){
        if(level<MAXLEVEL){
            level++;
        }
    }
    //取得当前电量
    public long getUnitLevelAvailable(){
        return level;
    }
    //停止充电
    public void StopEnergySource(){
        replenishTask.cancel(false);
    }
    //使用电量
    public boolean useEnergy(final long unit){
        if(unit>0&&unit<=level){
            level-=level;
            return true;
        }
        return false;
    }
}
package com.winter.Concurrency.April.TeachSample;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 *为保证FixEnergySource3类跨越内存栅栏,简单粗暴的方法是直接在方法面前加上synchronized关键字
 *写程序的座右铭是“先跑起来,然后再优化”
 *在下面4个方法前加上了synchronized关键字,因为这些函数需要访问level变量。
 */
public class FixEnergySource3 {

    private final long MAXLEVEL=100;
    private long level=MAXLEVEL;
    private static final ScheduledExecutorService replenishTimer= Executors.newScheduledThreadPool(10);
    private ScheduledFuture<?> replenishTask;
    private boolean keepRunning =true;
    //默认构造器私有化
    private FixEnergySource3(){

    }

    public static FixEnergySource3 create(){
        final FixEnergySource3 fixEnergySource2=new FixEnergySource3();
        fixEnergySource2.init();
        return fixEnergySource2;
    }
    //初始化周期充电的任务
    private void init(){
       replenishTask=replenishTimer.scheduleAtFixedRate(new Runnable(){
           @Override
           public void run() {
               replenish();
           }
       },0,1, TimeUnit.SECONDS);
    }
    //充电的函数
    public synchronized void replenish(){
        if(level<MAXLEVEL){
            level++;
        }
    }
    //取得当前电量
    public synchronized long getUnitLevelAvailable(){
        return level;
    }
    //停止充电
    public synchronized void StopEnergySource(){
        replenishTask.cancel(false);
    }
    //使用电量
    public synchronized boolean useEnergy(final long unit){
        if(unit>0&&unit<=level){
            level-=level;
            return true;
        }
        return false;
    }
}
package com.winter.Concurrency.April.TeachSample;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * FixEnergySource3虽然解决了跨越内存栅栏问题,但是并发性能不高。下面使用原子类来解决并发问题。
 *
 *
 */
public class FixEnergySource4 {

    private final long MAXLEVEL=100;
    private final AtomicLong level=new AtomicLong(MAXLEVEL);//定义原子类
    private static final ScheduledExecutorService replenishTimer= Executors.newScheduledThreadPool(10);
    private ScheduledFuture<?> replenishTask;
    private boolean keepRunning =true;

    private FixEnergySource4(){
    }

    public static FixEnergySource4 create(){
        final FixEnergySource4 fixEnergySource3=new FixEnergySource4();
        fixEnergySource3.init();
        return fixEnergySource3;
    }

    private void init(){
        replenishTask=replenishTimer.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                replenish();
            }
        },0,1, TimeUnit.SECONDS);
    }

    public void replenish(){
        if(level.get()<MAXLEVEL){
            level.incrementAndGet();//原子类增加
        }
    }
    //取得当前原子类的值
    public long getUnitLevelAvailable(){
        return level.get();
    }
    public synchronized void StopEnergySource(){
        replenishTask.cancel(false);
    }
    public boolean useEnergy(final long unit){
        final long currentLevel=level.get();
        if(unit>0&&unit<=currentLevel){
            return level.compareAndSet(currentLevel,currentLevel-unit);
        }
        return false;
    }

}
package com.winter.Concurrency.April.TeachSample;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 在一个变量的时候,原子类很好的解决了问题,现在需求增加,现在需要追踪并记录电源的使用情况,
 * 每次电源电量使用完的时候都必须把电源的使用次数累加。使用synchronized当然能解决问题,但是并发性并不好。
 *
 *
 */
public class FixEnergySource5 {

    private final long MAXLEVEL=100;
    private long level=MAXLEVEL;
    private long usage=0;//记录使用的次数
    private final ReadWriteLock monitor= new ReentrantReadWriteLock();//读写锁
    private static final ScheduledExecutorService replenishTimer= Executors.newScheduledThreadPool(10);
    private ScheduledFuture<?> replenishTask;

    private FixEnergySource5(){}

    public static FixEnergySource5 create(){
        final FixEnergySource5 fixEnergySource4=new FixEnergySource5();
        fixEnergySource4.init();
        return fixEnergySource4;
    }

    private void init(){
        replenishTask=replenishTimer.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                replenish();
            }
        },0,1, TimeUnit.SECONDS);
    }
    //变量改变,用写锁
    public void replenish(){
        monitor.writeLock().lock();
        try {
            if (level < MAXLEVEL) {
                level++;
            }
        }finally {
            monitor.writeLock().unlock();
        }
    }
    //只是读取变量
    public long getUnitLevelAvailable(){
        monitor.readLock().lock();
        try {
            return level;
        }finally {
            monitor.readLock().unlock();
        }
    }
    //读取使用的次数
    public long getUsageCount(){
        monitor.readLock().lock();
        try{
            return usage;
        }finally {
            monitor.readLock().unlock();
        }
    }
    public void StopEnergySource(){
        replenishTask.cancel(false);
    }
    //写操作
    public boolean useEnergy(final long unit) {
        monitor.writeLock().lock();
        try {
            if (unit > 0 && unit <= level) {
                level -= level;
                usage++;
                return true;
            }
            return false;
        }finally {
            monitor.writeLock().unlock();
        }
    }


}

 

呵呵。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值