博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程(4) - 多线程之Volatile关键字、ThreadLocal、Atomic系列类、CAS
阅读量:4164 次
发布时间:2019-05-26

本文共 4550 字,大约阅读时间需要 15 分钟。

Volatile关键字

      volatile关键字的作用是使得变量在多个线程间可见。

/**

 * Volatile关键字:使变量在多个线程间可见

 */

public class J18Volatile {

    public static void main(String[] args) throws InterruptedException {

        Volatile volatile1 = new Volatile();

        volatile1.start();

        Thread.sleep(1000);

       

        //

        //2、第一次运行:会发现设置为false依然是无效的,依然是还是处于运行状态

        //看回之前的线程运行图

        //JDK1.5给线程加了个独立线程空间,每次线程执行都是执行自己的独立空间的数据,

        //这也说明 了即便设置为false,执行结果依然是false,还是没有停止,因为此时该的是主内存的数据,

        //而不是线程独立空间的这个线程的值,即线程执行和自己的独立空间变量关系。

        //main方法的线程,volatile1.start()线程,总共2个线程

        volatile1.setRun(false);

        System.out.println(volatile1.isRun());

       

        //第二次运行,在添加了volatile关键字,会发现更改为false,就正常停止了,因为此时每次都是和主内存的变量交互

    }

}

class Volatile extends Thread{

    //1、未加volatile关键字

//  private boolean isRun = true;

   

    //3、添加volatile关键字

    private volatile boolean isRun = true;

 

    public void setRun(boolean isRun) {

        this.isRun = isRun;

    }

   

    public boolean isRun() {

        return isRun;

    }

 

    @Override

    public void run() {

        System.out.println("run...");

        int i = 0;

        while (isRun) {

            

        }

        System.out.println("线程停止");

    }

}

 

      以上的例子中可以看到,volatile关键字虽然拥有多个线程之间的可见性,即都从主内存中去拿同一个变量,但是不具备同步性(原子性),可以算得上是一个轻量级的synchronized,性能要比synchronized强,不会造成阻塞,但不能替代synchronized的同步功能,并且很多开源框架的代码都会用到volatile(netty)。

      但是需要原子性操作的时候,可以用Atomic系列类对象,这个对象就是具备原子性操作的,以下案例可以看到,10个线程同时操作一个方法,不用加锁,方法内是对int变量进行++操作,会出现数字不正确,而Atomic类型是可以保证数据的原子性。

 

Atomic系列类

      Atomic系列类对象是保证方法的结果的原子性,并不保证多次操作的原子性,即每个线程的操作结果不是按顺序的。

/**

 * Volatile关键字不具备synchronized关键字的原子性(同步性)

 */

public class J19VolatileNotAtomic {

    public static void main(String[] args) throws InterruptedException {

        //1、开启10个线程,会发现结果并不是10000

        VolatileNotAtomic volatileNotAtomic = new VolatileNotAtomic();

        for (int i = 0; i < 10; i++) {

             new Thread(volatileNotAtomic).start();

        }

       

        Thread.sleep(2000);

       

        //2、开启10个线程,会发现结果是10000

        VolatileAtomic volatileAtomic = new VolatileAtomic();

        for (int i = 0; i < 10; i++) {

             new Thread(volatileAtomic).start();

        }

       

        //3Atomic系列类对象是保证方法的结果的原子性,并不保证多次操作的原子性,即每个线程的操作结果不是按顺序的。

    }

}

class VolatileNotAtomic implements Runnable {

   

    private volatile int count;

   

    public void addCount() {

        for (int i = 0; i < 1000; i++) {

             count++;

        }

        System.out.println("Not" + count);

    }

 

    @Override

    public void run() {

        addCount();

    }

}

class VolatileAtomic implements Runnable {

   

    private AtomicInteger count = new AtomicInteger();

   

    public void addCount() {

        for (int i = 0; i < 1000; i++) {

             count.incrementAndGet();

        }

        System.out.println("Have" + count);

    }

 

    @Override

    public void run() {

        addCount();

    }

}

/**

 * Atomic系列类踩坑

 * Atomic类只保证本身(一个)方法的原子性,不保证多个操作的原子性

 */

public class J20AtomicClass {

    public static void main(String[] args) {

        AtomicClass atomicClass = new AtomicClass();

        for (int i = 0; i < 10; i++) {

             new Thread(atomicClass,"t" + i).start();

        }

    }

}

class AtomicClass implements Runnable {

   

    private AtomicInteger count = new AtomicInteger(0);

   

    public int add() {

        count.addAndGet(1);

        count.addAndGet(2);

        count.addAndGet(3);

        return count.get();

    }

 

    /*

     * 未加synchronized时,多个addAndGet在一个方法内是非原子性的。

     * 因此需要加synchronized修饰,保证多个addAndGet方法的整体原子性。

     */

    @Override

//  public void run() {

    public synchronized void run() {

        System.out.println(Thread.currentThread().getName() + " - " + add());

    }

}

 

      Atomic系列类的原理是通过自旋CAS(Compare And Swap)操作volatile变量实现的,即比较内存中的旧值与预期值是否相同进而决定是否交换。

      CAS就是比较内存中的对象和当前对象的值是否相同,相同的话才回更新内存中的值,不同的话则会返回失败。

      Atomic的问题,一是ABA问题,即对于一个旧变量值A,线程2将A的值改成B又改成A,此时线程1通过CAS看到A并没有变化,实际A已经发生变化了,而要解决这个问题的可以通过记录一下变量的版本就可以了,在变量值发生变化时,比较一下版本就知道变量有没有变化,其中AtomicStampedReference就是这样的思路,其中MySQL中的Innodb的多版本并发锁也是这样做的。

      二是Atomic系列类的自旋问题,Atomic类会尝试多次CAS操作,直至失败或成功,这个过程叫自旋;通过自旋过程可以看出自旋操作并不会把线程挂起,从而避免了内核线程切换,但是自旋的过程也可以看成CPU死循环,会一直占用CPU资源,在竞争并不激烈的情况下效率会大大提高,相反竞争激烈的情况下,失败率会大大提高,因此一般会有个失败次数限制的,也可以使用LongAddr类来替换,这个类采用的思想是分段锁来解决并发竞争的问题。

 

ThreadLocal类

      ThreadLocal是用来定义线程局部变量的,是一种在多线程并发访问变量的间接方法,与synchronized等加锁方式不同,ThreadLocal完全不提供所,而使用以空间换时间的手段,为每个线程提供便利的独立副本,以保证线程安全。

      从性能上来看,ThreadLocal并不具备优势,在并发不是很高的时候,加锁的性能会更好,但作为一套与所完全无关的线程安全解决方案,在高并发量或竞争激励的场景中,使用ThreadLocal可以在一定程度上减少所的竞争。

/**

 * ThreadLocal案例

 */

public class J21ThreadLocal {

    public static void main(String[] args) throws InterruptedException {

        ThreadLocalTest test = new ThreadLocalTest();

       

        new Thread(new Runnable() {

             @Override

             public void run() {

                 test.setValue("AA");

                 System.out.println(Thread.currentThread().getName() + " " + test.getValue());

             }

        },"t1").start();

       

        Thread.sleep(1000);

       

        new Thread(new Runnable() {

             @Override

             public void run() {

                 System.out.println(Thread.currentThread().getName() + " " + test.getValue());

             }

        },"t2").start();

       

    }

}

class ThreadLocalTest {

   

    private ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();

 

    public Object getValue() {

        return this.threadLocal.get();

    }

 

    public void setValue(Object value) {

        threadLocal.set(value);

    }

}

 

转载地址:http://llmxi.baihongyu.com/

你可能感兴趣的文章
flex常用网站
查看>>
flex 页面跳转
查看>>
flex 3.0中关于两个.mxml文件之间链接的简单方法
查看>>
使用viewstack
查看>>
Flex 学习站点汇总
查看>>
12个flex常用代码
查看>>
Flex项目开发资源
查看>>
[Flex]Flex编程注意之直接获取某个组件的对象(this[]用法)
查看>>
Flex4中获取任意组件的ID或其他属性值
查看>>
Flex4中获取任意组件的ID或其他属性值
查看>>
Flex4中获取任意组件的ID或其他属性值
查看>>
flex 设置背景图片
查看>>
Flex 4中组件背景设置(填充方式)group为例子
查看>>
Setting a background image on a Spark Application in Flex 4
查看>>
flash 遍历sprite
查看>>
loaderInfo 和 contentLoaderInfo 的区别
查看>>
flash 声音设置
查看>>
as2全局变量等
查看>>
as3.0中通过ApplicationDomain类获得被加载swf
查看>>
主swf与被加载swf的通信问题
查看>>