美文网首页
【技术笔记-线程1】基本概念、volatile 和 CAS

【技术笔记-线程1】基本概念、volatile 和 CAS

作者: 高大强19 | 来源:发表于2020-03-01 22:21 被阅读0次

    一、线程相关基本概念

    区分程序、进程、线程

    1.程序是保存在电脑里的一堆代码,静态的。比如,qq安装后,保存在c盘某个文件夹下一些文件。

    2.进程是程序运行起来加载在内存中的代码,是程序的载体。比如,双击QQ运行起来后,分配内存以供运行。

    3.什么是线程? 是资源的最小单位,一个进程可能一个或多条线程。

    三种启动线程的方式:1.Thread 2.Runnable 3.通过线程池来创建:Executors.newCachedThreadPool

    不加synchronized 会脏读,业务上允许脏读就不要加,效率会低百千倍。

    synchronized 重入锁,必须是, super时,不可重入就死锁了。

    程序出现异常,锁释放了。

    synchronized 在1.5实现是重量级的。

    锁升级概念:

    我就是厕所所长(1 2)

    markword 记录这个线程ID(偏向锁) 效率高。

    如果有线程征用,升级为自旋锁。 用马桶举例,循环转圈玩。默认旋10次。

    还没有得到这把锁,升级为重量级锁,去操作系统申请。等待状态,不占cpu了。

    锁只能升级,不能降级。

    什么情况下,用自旋锁好?

    自旋锁,占用cpu但是不占用操作系统,所以是用户态的,不经过内核态。os锁内核态。

    执行时间长,线程数多 用系统锁更好些。

    执行时间短,线程数少 用自旋锁synchronized 。

    synchronized锁的是对象

    不加任何,是锁当前this

    锁静态是this.xxx.class

    锁定方法和非锁定方法可同时执行

    锁升级

    偏向锁,自旋锁,重量级锁。

    synchronized(Object)

    锁的Object,不能用String常量,interger,Long

    String常量 会导致不同的线程访问同一个对象。

    要是实际工程中,直接规定,不能加头,用锁。

    二. volatile 和 CAS

    保证线程可见性

    MESI

    缓存一致性协议(cpu)

    禁止指令重排序(cpu)

    DCL单例

    new Thread(t::m, "t1").start();  //

    java 的 匿名函数lambda 2种写法:

    (Apple a) -> a.getWeight () Apple: :getWeight

    () -> Thread. currentThread()。dumpStack() Thread. currentThread() : :dumpStack

    (str, i) ->str. subatring(i) String: :subetring

    (String s) -> System.out .println(s) System.out :: println

    2.1

    加了volatile,内存每次都能读到。

    单例模式

    1. 饿汉式  private + get方法 

    初始化后,jvm就帮你初始化

    2.调用方法时初始化,懒汉式,带来线程不安全性。

    3.线程安全的,在get方法上加synchronized

    4.锁细化,先判断线程是否为空再new

    5.也是不对的,1 , 2 个线程都判断了,都会初始化。

    6.用双重检查,保证线程安全。

    超高并发的情况下,如,京东、淘宝的秒杀。会有指令重排序的问题。

    加volatile 不允许对这个对象的指令重排序。深追究,jvm的指令重排序,代码级别的,本质是使用cpu的读屏障、写屏障

    new 对象过程,分三步,1初始值 2成员变量赋真正初始值 3赋值给栈里变量INSTENCE

    volatile不能保证原子性,不能替代synchronized

    2.2 锁优化

    锁粒度变粗,变细

    争论不剧烈的情况下,变细为好。可以使线程争用时间变短,从而提高效率。

    当征用锁过多的情况下,锁为粗好。加大锁。

    对对象加锁,对象最好加final防止对象变化导致锁无效,异常。

    2.2.1 CAS无锁优化,自旋锁

    Atomic开头的都是CAS,常见AtomicInteger

    例:代码2.2.1

    原理:Compare And Set

    都是用Unsafe的CompareAndSetxxx,CompareAndExchangexxx等实现的。

    cas(v,Expected,NewValue)

    先判断是否是我期望的值,不是,说明在我访问中,有人改变了,要重新执行,要不就退出。

    思考:假如我判断符合期望,在准备读取的时候有人改变了呢?

    cas是cpu原语支持的,指令级别的,不能打断的。

    问: 期望值怎么来?比如,列表,往后插,判断索引是否是3. 一般根据业务逻辑判断。

    ABA问题,int 、long类型,无所谓,对象就有问题。

    解决:加版本号,

    A 1.0

    B 2.0

    A 3.0

    AtomicStampedReference 解决(https://www.cnblogs.com/zyrblog/p/9864932.html

    了解Unsafe:等同于c c++的指针

    典型的单例

    8jdk还是 CompareAndSet

    11jdk用了弱指针weakCompareAndSetInt,他的好处是垃圾回收的时候效率高。

    还有allocateMemory操作指针。。。

    分配   释放 分配   释放

    c -> molloc free  c++-> new delete

    图2.2.1 Unsafe

    代码2.2.1

    /**

    * 解决同样的问题的更高效的方法,使用AtomXXX类

    * AtomXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的

    * @author mashibing

    */

    package com.mashibing.juc.c_018_00_AtomicXXX;

    import java.util.ArrayList;

    import java.util.List;

    import java.util.concurrent.atomic.AtomicInteger;

    public class T01_AtomicInteger {

      /*volatile*/ //int count1 = 0;

      AtomicInteger count = new AtomicInteger(0);

      /*synchronized*/ void m() {

          for (int i = 0; i < 10000; i++)

            //if count1.get() < 1000

            count.incrementAndGet(); //count1++

      }

      public static void main(String[] args) {

          T01_AtomicInteger t = new T01_AtomicInteger();

          List<Thread> threads = new ArrayList<Thread>();

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

            threads.add(new Thread(t::m, "thread-" + i));

          }

          threads.forEach((o) -> o.start());

          threads.forEach((o) -> {

            try {

                o.join();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

          });

          System.out.println(t.count);

      }

    }

    相关文章

      网友评论

          本文标题:【技术笔记-线程1】基本概念、volatile 和 CAS

          本文链接:https://www.haomeiwen.com/subject/uanlkhtx.html