多线程与高并发(一)基础概念

24 篇文章 0 订阅
12 篇文章 0 订阅
本文详细探讨了Java中的同步工具,包括synchronized、volatile、锁升级及线程池。深入分析了synchronized的基本概念、线程同步、锁升级的原理,以及在不同场景下与自旋锁的选择。同时,讲解了volatile的作用,如保持线程可见性和禁止指令重排序,并讨论了DCL单例的各种实现方式。此外,还涉及到了字节码、CPU缓存一致性协议MESI以及Atomic类的CAS操作。
摘要由CSDN通过智能技术生成

基础概念(本章)

JUC同步工具(后续)

同步容器(后续)

线程池(后续)

高频面试和加分项(后续)

Disruptor  mq框架,单机性能最高,效率最好(后续)

个人学习记录,不喜勿喷

目录

day1

一,synchronized基本概念

二,线程同步

三,锁升级

四,问题

1,系统锁什么时候比自旋锁效率高?lock和synchroized 如何取舍

2,如何控制大对象

day2

一,volatile的作用

1,保持线程的可见性

2,MESI  cpu缓存一致性协议

3,禁止指令重排序(cpu)

二,DCL单例

1.饿汉式

2.懒汉式

3.懒汉式+方法锁

4.懒汉式+代码块锁

5.双重检查Double Check Lock

6.双重检查+volatile 

三,创建对象JVm三个步骤

四,synchroized ,volatile  优缺点

五,字节码

        1,如何查看字节码视图?

2,loadfence原语指令 ,storefence原语指令

3,CAS(无锁(乐观锁)优化 自旋)

4,ABA问题

5,Unsafe (恶汉单例)


day1

线程概念,常用方法,线程状态

一,synchronized基本概念

不能用String常量,Integer(值变了,就会变一个新对象) Long

"object"

String常量,所有用到都是同一个,假如都是一个类库的,不同的代码会是用的一把锁,死锁。

Object=null时能当锁么?编译不过,空指针异常

二,线程同步

-synchronized

.锁的是对象(类的前两位)不是代码

.thie  Object.class

.锁定方法  非锁定方法,可以同时执行

.异常对同步块的影响

抛出异常会释放锁,会导致数据不一致,使用catch包裹可避免。

三,锁升级

synchroized的底层实现:

早期JDK: 重量级:OS找操作系统去申请锁;

改进1.5之后:  锁升级的概念-,搜索文字《我就是厕所所长》 马士兵

synchronized(object)

markword  对象头上前两位记录锁的类型,还记录这个线程ID  (偏向锁)

如果有线程争用 : 升级为  自旋锁(占用CPU但不占用资源)

自循环while,10次后升级为重量级锁-OS

升级后,大多数情况下效率是很好的

只能升级,无法降级。两个线程都升级为OS之后,降到自旋锁效率会更高,但是降不下来了。

四,问题

1,系统锁什么时候比自旋锁效率高?lock和synchroized 如何取舍

执行时间长,线程数量少。反之自旋锁效率高

lock             :CAS 一直自旋,会占用cpu

synchroized :升级到OS之后,会进入wait队列,不占用cpu时间

2,如何控制大对象

XX:PretenureSizeThreshold 默认值和作用

超过这个值对象直接在old区分配内存

默认值始0,意思是不管多大都先在eden中分配内存

day2

一,volatile的作用

1,保持线程的可见性

线程执行时,会产生副本,修改自身副本里的内容,读取中会被别人线程修改,导致值丧失一致性

加volatile之后,没次写都会被线程读到。

线程A对一个volatilevolatilevolatile变量的修改,对于其它线程来说是可见的(每次获取最新的)

2,MESI  cpu缓存一致性协议

在早期的CPU中,是通过在总线加LOCK#锁的方式实现的,但是这种方式开销太大,所以Intel开发了缓存一致性协议,也就是MESI协议。

3,禁止指令重排序(cpu)

并发执行指令,编译器把指令重新排序。汇编语言的重排序

二,DCL单例

保证在JVM内存中,永远只有某一个类的一个实例。例如:权限

1.饿汉式

private static Object ob = new Object();

pulic static  Object getOb(){
    return this.ob ;
}

static保证只有一个实例,JVM做初始化。

不使用的时候会被实例化,要求使用的时候初始化

2.懒汉式

private static Object ob;

pulic static  Object getOb(){
    if(ob==null){
       tob = new Object ();
    }
    return this.ob ;
}

多线程会出现问题,需要线程安全

3.懒汉式+方法锁

get的时候添加synchroized

private static Object ob;

pulic static  Object  synchroized   getOb(){
    if(ob==null){
       tob = new Object ();
    }
    return this.ob ;
}

锁在方法上,方法实现如果太多代价太重

4.懒汉式+代码块锁

在实例化的时候添加synchroized 块

private static Object ob;

pulic static  Object  getOb(){
    if(ob==null){
        synchroized(this){
          tob = new Object();
        }   
    }
    return this.ob ;
}

仍有问题,多线程下为空时,会发生在同步块中同时new

5.双重检查Double Check Lock

第一重判空的必要性:不申请锁,提高效率,多数情况下是不为空的。

private static Object ob;

pulic static  Object  getOb(){
    if(ob==null){
        synchroized(this){
            if(ob==null){
              tob = new Object();
            }
        }   
    }
    return this.ob ;
}

很难复现,但依然会出现问题,问题出现在指令重排序上。ASM在三个指令码之间设置睡眠

6.双重检查+volatile 

private static volatile Object ob;

pulic static  Object  getOb(){
    if(ob==null){
        synchroized(this){
            if(ob==null){
              tob = new Object();
            }
        }   
    }
    return this.ob ;
}

三,创建对象JVm三个步骤

1,给这个对象申请内存                  :a=0

2,给这个对象的成员变量初始化     :a=8

3,把这块内存的内容分配给这个对象  :栈内存指向a=8的地址

如果发生指令重排序,二三步颠倒了,半初始化的时候就赋值了,会发生还没有初始化的时候就把指向指定到a=0的地址,这个对象就不为空了,多线程读取的值是不同的。

相当于把半成品发布,虽然这个半成品会逐渐完善,但这个过程中充满了不确定。

帮助开阔思路,理解虚拟机。实际工作中直接用饿汉式,不会差那么一个对象的内存。

四,synchroized ,volatile  优缺点

synchroized 保证原子性,但是不能阻止重排序。

volatile 并不能保证多个线程共同修改变量时锁带来的不一致问题,都看到是同一个,但会同时做一样的事情,没有原子控制,多人做了重复的事情。(案例:多线程不用同步,操作一个volatile 修饰的变量。在操作方法上添加同步可以解决)

synchroized ZED优化

细化:业务逻辑中,sync加在具体 操作资源上,而不是整个方法;

粗化:业务逻辑中有多个块锁,整合在一起,减少系统征用。(查询数据库表时,每一行都加锁,不如加在表上)

锁定义时,如果发生改变,影响锁的使用

锁是锁定对象额头两位,锁本身发生变化时,会影响锁的使用。解决办法是添加final关键字

五,字节码

1,如何查看字节码视图?

 打开idea 中的settings > plugins 搜索 jclasslib Bytecode  Viewer 插件 安装重启生效

view  -  Show Bytecode With jclasslib  -

public static void main(String argv[]){
    T t =   new T();
    t.setA(8);
}

 0 new #2 <com/example/demo/com/T>
 3 dup
 4 invokespecial #3 <com/example/demo/com/T.<init>>
 7 astore_1
 8 aload_1
 9 bipush 8
11 invokevirtual #4 <com/example/demo/com/T.setA>
14 return

Mgr06.java

2,loadfence原语指令 ,storefence原语指令

读写屏障

修饰引用类型时,一个线程内修改引用对象内的值,另一个线程无法获知,除非引用本身发生变化。

volatile int[] 当做锁,数组发生变化时,对锁的影响。

3,CAS(无锁(乐观锁)优化 自旋)

AtomXXX类

AtomicInteger

int类型的原子性操作,不用添加sync和volatile

.Compare  And Set

比较和设置

.cas(V,Expected,NewValue)

当前值是我期望的值,操作该值。我设置的时候没人改变他的值,他是我期望的时候操作。
 


cas(V,期望值,更新值){
    Expected  = read v;

    for(;;){

       if V == Expected 
            V=NEW
       else
           不是期望的值,需要重新读取
     }
}

OTHERWISE TRY AGAIN OR FAIL

CPU原语支持

cpu指令级别,不能被打断

4,ABA问题

先把1变成2,然后变成1。版本号

基础类型没问题,如果是引用就会有问题。object,对象的引用被改变,实际地址就改变了。(女朋友跟你复合,中间经历了别的女人,指针发生变化了?)

A 1.0

B 2.0

A 3.0

AtomicStampedReference 

5,Unsafe (恶汉单例

不能直接使用,只能在反射的时候使用(8,11可以使用)。

使用了弱指针的优势,垃圾回收的时候效率高。

操作内存,具备了C++的能力。

分配释放内存

allocateMemory freeMemory     - >   c  malloc free      - >  c++ new delete

Unsafe = c  c++的指针 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值