java基础之线程 认识原子类

把java基础撸一边,从简单的开始。html

线程部分:java

学习原子类,先来了解一下一个概念,乐观锁与悲观锁。面试

乐观锁与悲观锁:

乐观锁:

简单理解:就是认为该数据在获取的时候不会被其余线程修改,更新的时候判断一下就能够了。是否同样,像是缓存。更新给个标志就能够。在前面的介绍的volatile就是个乐观锁,自认为不会被其余线程修改,其余线程读就不会出现脏数据的出现。乐观锁,感受就像是个缓存数据同样,会更新,明知会更新,但取的时候放心大胆乐观地取。算法

悲观锁

简单理解,就是和乐观锁相反。其余线程一定会对数据进行修改,不敢放心大胆得取数据,因此要多这个数据要强行锁住,像是synchronize,对线程进行互斥。保证数据读取的时候不会被修改。数组

推荐文章:乐观锁与悲观锁缓存

原子类:

悲观锁这样对数据进行了保护,操做数据的时候,只有一个线程在场。synchronize就证实了这点,可是这样让其余线程堵塞又执行是很是消耗性能的。那么乐观锁是怎么保证乐观锁的,volatile是确定不行的。bash

volatile,这个修饰词只能保证可见性。不能保证原子性。并发

实例<一>:

public class Demo41 {

    public volatile int val = 0 ;

    public void set(){
        val++;
    }

    public static void main(String[] age){
        Demo41 demo41 = new Demo41();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (;;){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    demo41.set();
                    System.out.println(Thread.currentThread().getName() + " val : "+demo41.val);
                }

            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (;;){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    demo41.set();
                    System.out.println(Thread.currentThread().getName() + " val : "+demo41.val);
                }
            }
        }).start();
        ...
    }

}复制代码

打印结果:ide

Thread-2   val : 12
Thread-0   val : 14
Thread-1   val : 14
Thread-2   val : 15
Thread-0   val : 17
复制代码

java除了synchronize提供原子性,还提供了不少。好比原子类系列性能

import java.util.concurrent.atomic.Atomic**;复制代码

实例<二>:

public class Demo41 {

    public AtomicInteger val = new AtomicInteger(0) ;

    public int set(){
        return val.getAndIncrement();//自加1
    }

    public static void main(String[] age){
        Demo41 demo41 = new Demo41();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (;;){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + " val : "+demo41.set());
                }

            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (;;){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " val : "+demo41.set());
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (;;){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " val : "+demo41.set());
                }
            }
        }).start();
    }

}复制代码

打印

Thread-0   val : 0
Thread-1   val : 1
Thread-2   val : 2
Thread-0   val : 3
Thread-1   val : 4
Thread-2   val : 5
Thread-0   val : 6复制代码

简单看源码:

AtomicIn系列有不少,下面是AtomicInteger的源码。我拿个比较简单的讲,标题是简单看源码。

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
private volatile int value;复制代码

发现:

1:继承了Number。这个类有一些对数值类型的操做。

2:实现了Serialzable接口。

3:成员变量建立了Unsafe。

4:成员变量中有Long类型valueOffset,private修饰,不能被其余类改动,能猜到这个字段就是这个类操做的核心了。

4:静态代码块中,对valueOffset作了处理。unsafe.objectFieldOffset(File)这个方法,获取该属性在内存中的偏移位置。Long类型用来存储这个获取的地址。

5:拥有private和volatile修饰的value int类型值,也就是这个类计算的值了。

使用方法探究:

在上面实例的代码中,有两处使用一个是实例化的时候,一个是自增的时候。

public AtomicInteger val = new AtomicInteger(0) ;

public int set(){
    return val.getAndIncrement();
}复制代码

看构造方法的源码

public AtomicInteger(int initialValue) {
    value = initialValue;
}复制代码

只是个普通的复制功能。注意,发现5

在来看自加方法

/**
 * Atomically increments by one the current value.
 *
 * @return the previous value
 */
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}复制代码

这里使用到了unsafe类的操做。进行查看这个方法的源码

//内存地址V,旧的预期值A,即将要更新的目标值B。
/*public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = this.getIntVolatile(o, offset); //拿到内存位置的最新值
        //拿到内存位置的最新值v,使用CAS尝试修将内存位置的值修改成目标值v+delta,若是修改失败,
则获取该内存位置的新值v,而后继续尝试,直至修改为功
    } while(!this.compareAndSwapInt(o, offset, v, v + delta));

    return var5;
}*/复制代码

这里看到了的是CSA算法,即比较并替换,是一种实现并发算法时经常使用的技术。

这个比较是个do...while循环比较,这里能够看到那个Long类型存地址是干吗的了,就是为了取数据的,得到如今内存存取的值,而后拿须要修改的值进行比较,若是是样的说明修改为功,若是没有修改为功,说明还有人改,因此继续循环(没有作重量锁,如synchronize因此会有多个线程进入)上面的注释写了,你们放大点看,也能够从下面推荐文章中看。

还要注意,为何能够及时通知到数据的更改,是拥有有volatile修饰,可见性。在建立原子类的时候,value被volatile修饰。不得不说句,牛逼。

原子类系列,还有能够操做对象,数组。大同小异,就不一一介绍了。谢谢各位观看。

推荐文章:面试毕问的CAS,你懂吗?

相关文章
相关标签/搜索