Java线程-线程池-原子操作

原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。
int++并不是一个原子操作,所以当一个线程读取它的值并加1时,另外一个线程有可能会读到之前的值,这就会引发错误。
为了解决这个问题,必须保证增加操作是原子的,可以通过volatile、synchronized关键字来解决并发访问的安全问题,但是这样解决太麻烦。
java.util.concurrent.atomic包提供了int和long类型的装类,它们可以自动的保证对于他们的操作是原子的并且不需要使用同步。

下面给出一个反面例子:

public class Main {
    public static void main(String args[]){
        //创建一个线程池
        ExecutorService executorService= Executors.newFixedThreadPool(2);
        //创建五个线程
        Runnable runnable1=new RunnableImpl("张三",2000);
        Runnable runnable2=new RunnableImpl("李四",1000);
        Runnable runnable3=new RunnableImpl("王五",3000);
        Runnable runnable4=new RunnableImpl("赵六",4000);
        Runnable runnable5=new RunnableImpl("陈七",2000);
        Runnable runnable6=new RunnableImpl("王八",2000);
        Runnable runnable7=new RunnableImpl("毛九",5000);
        //执行各个线程
        executorService.execute(runnable1);
        executorService.execute(runnable2);
        executorService.execute(runnable3);
        executorService.execute(runnable4);
        executorService.execute(runnable5);
        executorService.execute(runnable6);
        executorService.execute(runnable7);
        //关闭线程池
        executorService.shutdown();
    }

}
class RunnableImpl implements Runnable {
    private static AtomicLong atomicLong=new AtomicLong(10000);
    private String name;    //操作人
    private int x;          //操作金额
    RunnableImpl(String name,int x){
        this.name=name;
        this.x=x;
    }
    @Override
    public void run() {
        System.out.println(name+"执行了"+x+",当前余额:"+atomicLong.addAndGet(x));
    }
}

运行结果如下:

张三执行了2000,当前余额:12000
王五执行了3000,当前余额:16000
赵六执行了4000,当前余额:20000
陈七执行了2000,当前余额:22000
王八执行了2000,当前余额:24000
毛九执行了5000,当前余额:29000
李四执行了1000,当前余额:13000
张三执行了2000,当前余额:12000
李四执行了1000,当前余额:13000
王五执行了3000,当前余额:16000
赵六执行了4000,当前余额:20000
陈七执行了2000,当前余额:22000
王八执行了2000,当前余额:24000
毛九执行了5000,当前余额:29000

从结果可以看出,这样的计算结果还是存在问题,原子量虽然保证了单个变量在某一个操作上的安全,但是它无法保证整个程序的安全性,下面对错误进行修正:

public class Main {
    public static void main(String args[]){
        //创建一个线程池
        ExecutorService executorService= Executors.newFixedThreadPool(2);
        //创建一个锁对象
        Lock lock=new ReentrantLock(false);
        //创建五个线程
        Runnable runnable1=new RunnableImpl("张三",2000,lock);
        Runnable runnable2=new RunnableImpl("李四",1000,lock);
        Runnable runnable3=new RunnableImpl("王五",3000,lock);
        Runnable runnable4=new RunnableImpl("赵六",4000,lock);
        Runnable runnable5=new RunnableImpl("陈七",2000,lock);
        Runnable runnable6=new RunnableImpl("王八",2000,lock);
        Runnable runnable7=new RunnableImpl("毛九",5000,lock);
        //执行各个线程
        executorService.execute(runnable1);
        executorService.execute(runnable2);
        executorService.execute(runnable3);
        executorService.execute(runnable4);
        executorService.execute(runnable5);
        executorService.execute(runnable6);
        executorService.execute(runnable7);
        //关闭线程池
        executorService.shutdown();
    }

}
class RunnableImpl implements Runnable {
    private static AtomicLong atomicLong=new AtomicLong(10000);
    private String name;    //操作人
    private int x;          //操作金额
    private Lock lock;
    RunnableImpl(String name,int x,Lock lock){
        this.name=name;
        this.x=x;
        this.lock=lock;
    }
    @Override
    public void run() {
        lock.lock();
        System.out.println(name+"执行了"+x+",当前余额:"+atomicLong.addAndGet(x));
        lock.unlock();
    }
}

运行结果如下:

张三执行了2000,当前余额:12000
王五执行了3000,当前余额:15000
赵六执行了4000,当前余额:19000
陈七执行了2000,当前余额:21000
王八执行了2000,当前余额:23000
毛九执行了5000,当前余额:28000
李四执行了1000,当前余额:29000

猜你喜欢

转载自blog.csdn.net/qq_24630433/article/details/88421233