Java基础总结

1,Java JMM(java内存模型)

这个内存模型搞起来还是有点弯弯绕,一点点的整理吧。JMM的目的就是保证共享变量在多线程环境下的原子性,可见性,与有序性而定义的一组规范。这些问题的出现都跟CPU的设计有关系,CPU是计算机的运算核心所有的操作或者说指令都是在CPU里面运行的,而且CPU的运行效率超级高,比内存的运行速率还要高出几个数量级,做开发的肯定都知道内存比硬盘快几个数量级,会使用内存缓存来提高运行速率,但是使用了内存缓存就会出现数据不一致的问题,我们会采取各种措施来保证数据的一致性,这里不展开讨论缓存的问题。还是说回CPU,既然CPU的运行速率比内存快的多,设计者也设计了CPU的高速缓存来充分利用CPU的性能提高程序的运行效率。多了这个高速缓存,同样也会出现数据不一致的问题,于是就有了缓存一致性协议如MESI协议等,就是保证数据的一致性。各种操作系统运行在CPU等硬件之上同样要解决这个问题,于是不同的操作系统也有自己的内存模型。而java程序运行在JVM之上,而且支持平台一致,支持多线程并发,为了保证各个平台运行结果一致,保证共享数据在多线程环境下的一致有序,制定了JMM规范,所有的JVM都要遵从该规范,保证java程序的平稳高速运行。我们平时开发用到的(我现在接触到的)就是一个volatile和syncronized两个关键字,还有锁机制。当然还有其他原则,这里不涉及。而volatile的作用就是保证共享数据的可见性,使用的技术叫做内存屏障。内存屏障这个词听起来不是很好理解是英语直译,用中国话说就是内存数据的写入和读取的顺序以及方式,你不是有主内存还有cpu缓存还有什么堆内存栈内存吗?好一个内存屏障的指令可以规定数据从哪里读取,又该啥时同步。好了到这里了,一个 volatile 关键字的demo,贴在下面:

/**
 * volatile 可以保证共享变量的可见性,所谓可见性就是 保证变量的操作都是在共享堆内存中进行,而不是线程变量。
 */
public class VolatileTest {

    private static boolean flag = false;
    private static volatile boolean volatileFlag = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            while (!flag) { //程序会在这里死循环,因为之后的代码虽然对该属性做了修改,但对于该线程不可见。
            }
            System.out.println("Flag is now true!");
        }).start();

        new Thread(() -> {
            while (!volatileFlag) { //使用volatile修饰的变量只要发生变化对所有线程可见。
            }
            System.out.println("volatileFlag is now true!");
        }).start();

        Thread.sleep(1000);

        flag = true;
        System.out.println("Flag is set to true!");

        volatileFlag = true;
        System.out.println("volatileFlag is set to true!");
    }
}
2,ThreadLocal 与 SimpleDateFormat

首先是平时经常使用的 SimpleDateFormat 是线程不安全的,我平时都是现用现new的,据说这样子有点浪费系统资源。查阅相关资料后发现还可以结合ThreadLocal来使用,就是将df对象绑定在线程上,在空间和效率之间取得一点平衡。ThreadLocal,这个东西设计的很奇怪。你本身是用来操作Thread类里面的threadLocalMap的,你叫个ThreadLocalMapUtils不比ThreadLocal好理解啊?总之这个类就是将一个对象绑定在线程上,不同的线程会创建不同的对象(也有可能是克隆如果实现了clone方法的话)比如下面的例子。也就是说所有线程不安全的类(变量)都可以绑定在ThreadLocal上面呗?


import java.text.DateFormat;
import java.text.SimpleDateFormat;

/**
 * ThreadLocal 为每一个线程提供了独立的变量副本。也就是说 get() set() 方法是基于线程的,线程不一样获取到的对象就不一样。
 * 使用场景:
 * 适合保存和传递每个线程独立的数据,比如基于线程执行的用户信息,会话信息等,注意如果内部启用了新线程(子线程)会导致数据不一致。
 * 而且基于线程池的线程对象不会被回收,ThreadLocal变量也就不会被回收,所以使用完毕手动清除是个好习惯 threadLocal.remove();
 *
 */
public class ThreadLocalTest {

    static ThreadLocal<MyObject> threadLocal = ThreadLocal.withInitial(() -> {
        MyObject myObject = new MyObject();
        System.out.println(myObject);//com.kuafu.MyObject@22f71333 //com.kuafu.MyObject@29702e06 这里会执行两次,因为有两个线程来访问
        return myObject;
    });

    private static ThreadLocal<DateFormat> tlDf = ThreadLocal.withInitial(() -> {
        final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(simpleDateFormat);//java.text.SimpleDateFormat@4f76f1a0 //java.text.SimpleDateFormat@4f76f1a0
        return simpleDateFormat;
    });


    public static void main(String[] args) {
        final MyObject myObject = threadLocal.get();
        System.out.println(myObject);//com.kuafu.MyObject@22f71333

        new Thread("t1"){
            @Override
            public void run() {
                final MyObject myObject1 = threadLocal.get();
                System.out.println(myObject1); //com.kuafu.MyObject@29702e06
                System.out.println(myObject == myObject1); //false
                System.out.println(myObject.equals(myObject1)); //false
            }
        }.start();

        final DateFormat dateFormat = tlDf.get();
        System.out.println(dateFormat); //java.text.SimpleDateFormat@4f76f1a0

        new Thread("t1"){
            @Override
            public void run() {
                final DateFormat dateFormat1 = tlDf.get();
                System.out.println(dateFormat1); //java.text.SimpleDateFormat@4f76f1a0
                System.out.println(dateFormat == dateFormat1); //false 这里看起来是个浅克隆?
                System.out.println(dateFormat.equals(dateFormat1)); //true
            }
        }.start();
    }

}

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lx18854869896

和谐社会靠你了,老铁...

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值