java线程原子锁

今天被问到这样一个问题,两个线程共用一个int类型的变量,怎么保证数据安全?(不可以用synchronized关键字)你知道原子锁吗?

1. 例子引入

public class RunThread extends Thread {

    private boolean isRunning = true;

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }

    @Override
    public void run() {
        System.out.println("进入到run方法中了");
        while (isRunning == true) {
        }
        System.out.println("线程执行完成了");
    }
}

public class Run {
    public static void main(String[] args) {
        try {
            RunThread thread = new RunThread();
            thread.start();
            Thread.sleep(1000);
            thread.setRunning(false);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

现在有两个线程,一个是main线程,另一个是RunThread。它们都试图修改 第三行的 isRunning变量。按照JVM内存模型,main线程将isRunning读取到本地线程内存空间,修改后,再刷新回主内存。
而在JVM 设置成 -server模式运行程序时,线程会一直在私有堆栈中读取isRunning变量。因此,RunThread线程无法读到main线程改变的isRunning变量从而出现了死循环,导致RunThread无法终止。
解决方法,在第三行代码处用 volatile 关键字修饰即可。这里,它强制线程从主内存中取 volatile修饰的变量:

volatile private boolean isRunning = true;

2. volatile
所谓原子性,就是某系列的操作步骤要么全部执行,要么都不执行。
volatile就是非原子性的
比如,变量的自增操作 i++,分三个步骤:
①从内存中读取出变量 i 的值
②将 i 的值加1
③将 加1 后的值写回内存
这说明 i++ 并不是一个原子操作。因为,它分成了三步,有可能当某个线程执行到了第②时被中断了,那么就意味着只执行了其中的两个步骤,没有全部执行。
所以仅仅使用volatile关键字并不能保证的安全。
3. AtomicInteger
这个类的源码:

 public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return true if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

这段代码写的很巧妙:
compareAndSet方法首先判断当前值是否等于current;
如果当前值 = current ,说明AtomicInteger的值没有被其他线程修改;
如果当前值 != current,说明AtomicInteger的值被其他线程修改了,这时会再次进入循环重新比较;
使用:
AtomicInteger atomicInteger = new AtomicInteger(1);
4、java提供的原子操作

  • 原子更新数组,Atomic包提供了以下几个类:
    AtomicIntegerArray
    AtomicLongArray
    AtomicReferenceArray
  • 原子更新引用类型,也就是更新实体类的值,比如
    AtomicReference:原子更新引用类型的值
    AtomicReferenceFieldUpdater:原子更新引用类型里的字段
    AtomicMarkableReference:原子更新带有标记位的引用类型
  • 原子更新字段值
    AtomicIntegerFieldUpdater:原子更新整形的字段的更新器
    AtomicLongFieldUpdater:原子更新长整形的字段的更新器
    AtomicStampedReference:原子更新带有版本号的引用类型的更新器

5、应用

Java原子操作类 AtomicInteger 在实际项目中的应用。HttpClientFacotryBean工厂会工作在多线程环境中,生成Httpclient,
就相当于建立HttpClient连接,通过工厂模式控制HttpClient连接,能够更好的管理HttpClient的生命周期。而我们使用java原子
操作类AtomicInteger来控制计数器,就是为了保证,在多线程的环境下,建立HttpClient连接不会出错,不会出现2个线程竞争一个
HttpClient连接的情况。
看到一篇说线程的文章,觉得不错,分享一下:
http://www.jianshu.com/p/40d4c7aebd66

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值