关于Volatile关键字代码证明可见性,非原子性,禁止指令重排

Volatile关键字

在这里插入图片描述

1、证明内存可见性代码实现

import java.util.concurrent.TimeUnit;

/**
 * @author fighting
 * @date 2020-11-16-21:32
 * 	  1、如果一个线程对程序中的值进行更改,另外一个线程检测到那么就直接可以证明内存可见
 */

public class VisibilityDemo {
    public static void main(String[] args) {
        UpData upData = new UpData();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//            该线程让值增加到6
            upData.uptosix();
            System.out.println(Thread.currentThread().getName() + "已经更改值为" + upData.a);

        }).start();


        while (upData.a == 0) {
            //如果main线程检测到值发生改变就跳出循环程序结束,如果程序一直在运行则证明main线程检测到值改变
            //循环中不能写sout语句,因为sout语句中存在synchronized 锁,不能证明volatile内存可见性
        }

        System.out.println("main线程检测到值改变!证明了内存可见性。");

    }
}

class UpData {
//    去掉volatile关键字  线程序不会停掉  但是加上后程序会一直执行
    volatile int a = 0;

    public  int uptosix() {
        a =  6;
        return a;
    }
}

2、代码证明非原子性

import com.util.ThreadPollUtil;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Stream;

/**
 * @author fighting
 * @date 2020-11-14-23:02
 * 1、使用线程池的方法来验证+1操作的原子性
 *
 */
public class GrowOnePoll {
    private static volatile int flag = 0;

    public static void main(String[] args) throws InterruptedException {
//        创建线程计数器
        CountDownLatch downLatch = new CountDownLatch(1);
//        使用线程池创建线程
        ThreadPoolExecutor executor = ThreadPollUtil.executor;



            executor.submit(()->{
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                for (int i = 0; i < 100; i++) {
                    upone();
                }
                downLatch.countDown();
                System.out.println("执行" + downLatch.getCount());
            });



        downLatch.await();
        System.out.println(downLatch.getCount());
        System.out.println(flag);
        executor.shutdownNow();

        Thread thread = Thread.currentThread();
        ThreadGroup threadGroup = thread.getThreadGroup();
        int count = threadGroup.activeCount();
        Thread[] threads = new Thread[count];
        threadGroup.enumerate(threads, true);
        System.out.println(count);
        Stream.of(threads).forEach(x -> System.out.println(x.getName()));

    }

    public static synchronized int upone() {
        flag++;
        return flag;
    }
}

2.1 线程池工具类

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author fighting
 * @date 2020-11-14-22:41
 */
public class ThreadPollUtil {

//    返回java虚拟机处理器的数量
    private static int corePoolSize = Runtime.getRuntime().availableProcessors();

    /**
     * 手动通过 ThreadPoolExecutor 创建线程池
     *
     * corePoolSize     用于指定核心线程数量
     * maximumPoolSize  指定最大线程数
     * keepAliveTime    指定线程空闲后的最大存活时间
     *
     * 可以根据项目的需要自定义线程池的参数
     */
    public static ThreadPoolExecutor executor  = new ThreadPoolExecutor(
            corePoolSize,
            corePoolSize+1,
            10L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(1000));

}

3、禁止指令重排序,用该方法进一步增强单例模式的安全性
使用DCL已经可以保证单例模式的安全性,但是在及其微小的情况可能下会出现因为java在编译的时候因为指令重排序而造成的你编写的代码执行顺序不同,出现意外情况,所以在要使用的单例对象前加上volatile锁,禁止其编译后指令重排。

/**
 * @author fighting
 * @date 2020-11-11-16:32
 *
 *
 * 1、使用DCL(Double Check Lock 双端检锁机制)创建多线程安全的单例模式
 * 2、使用volatile禁止指令重排 保证顺序执行再次对单例模式进行巩固
 *
 */
public class DCLSingle {
    private static volatile DCLSingle ds = null;

    public DCLSingle() {
        System.out.println("创建该单例的实例。。。。。");
    }

    public static DCLSingle getDs() {
//        双重锁校验
        if (ds == null) {
            synchronized(DCLSingle.class) {
                if (ds == null) {
                    ds = new DCLSingle();
                }
            }
        }
        return ds;
    }

    public static void main(String[] args) {

//		测试多线程情况下会不会打印两次 创建单例对象。。。
        for (int i = 0; i < 200 ; i++) {
            new Thread(() -> {
                DCLSingle.getDs();
            }, String.valueOf(i)).start();
        }

    }
}

文章中如果出现错误欢迎指出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值