如果一个变量可以给多个线程读写,则我们说这个变量是共享的。下面通过一个栗子一步一步认识共享变量。
这是一个关于电源的控制程序,可以充电,用电,取得当前电量,停止充电。
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();
}
}
}
呵呵。。。