你需要了解的多线程知识(JAVA ) 复习

Volatile 关键字

Volatile 是java虚拟机提供的轻量级同步机制(保证可见性不保证原子性禁止指令重排

可见性之前需要了解

JVM(java虚拟机)

JMM(java内存模型) javamemory model 不真实存在描述的一种规则规范 定义了程序中各个变量(包括实例字段静态字段和构成数组对象的元素)的访问方式

JMM的同步的规定

1 线程解锁前必须把共享变量的值刷新回主内存

2 线程加锁前必须读取主内存的最新值到自己工作内存

3 加解锁是同一把锁

可见性:举个栗子 比如你new了一个student对象 要设置里面的age(默认为15) 的值 new的对象是在主内存中(我们暂时理解主内存是一个大的空间是一种共享区域内存所有线程可以访问),此时有两个线程需要更改student 中age的值 线程一设置的时候会从主内存copy一份student 并且更改age的值为18 更改完成后写到主内存中(另一个线程无法读这个线程里面的值)此时主内存的值被改变此时jmm通知另个线程age已经被更改为18 拎一个线程里面的age也变为了18

不保证原子性: 比如1000线程对一个变量进行++操作 如果是原子性的话会输出1000但是实际值一般不会是1000;(原因是 在一个线程取到值加完后要写的主内存的瞬间被挂起其他线程写入 此时还没有被通知 相当于同样的一个值被覆盖了....自行理解)如何解决 最简单的加上syn关键字 或者用Atomicinteger、AtomicLong .....等一些带原子类型的包装类详情百度

禁止指令重排: 重排的意思是例如x=3 y=4 x++ y++ 这四句可能翻译后的字节码为1324 (4个语句顺序) 也可能为1234 指令重排后的就为1324 假如多线程的话会导致数据不一致

(底层如何实现屏障 通过插入内存屏障禁止在内存屏障天后的指令执行重排优化 写之后加入store 读之后加入load)



单例模式多线程问题(懒汉式线程不安全)

懒汉式多线程获得的不是同一个对象导致不是单例了

单例模式多线程下无效

可以用DCL(双端检锁)但是不一定线程安全,原因是有指令重排的存在加volatile可以禁止指令重排(加入双端检锁也可能发生不安全 因为在你new完的时候 可能切换线程此时实例还是为null 另一个也new 了一个 这样不就两个实例了吗)还有一个重排问题就是可能导致实例为null 需要在instance 前加volatile

安全的代码

public class VolatileMain {
    private static volatile   VolatileMain instance=null;//不加这个volatile可能导致重排对象为null

    private VolatileMain()
    {
        System.out.println(Thread.currentThread().getName()+"构造方法");
    }
    //DCL 双端检测-可以用双端检测加锁前后都进行判断 锁机制 会好很多
    public static VolatileMain getInstance() //多个线程就不是单例模式想要单例需要方法加上synchronize关键字
    {
        if (instance==null)
        {
            synchronized (VolatileMain.class)
            {
                if (instance==null)
                {
                    instance=new VolatileMain();
                }

            }

        }

        return instance;
    }



CAS知道吗如何实现?(cpu并发原语)

CAS 是判断内存的某个位置是否为预期值如果是这个值就改为新址这个过程是原子性的cas并发原语体现在java语言中就是sun.unsafe 类中的各个方法调用unsafe方法的cas方法这是一种完全依赖于硬件的功能通过它实现了原子操作 cas是系统原语 由若干条指令组层 实现某一功能

CAS比较并交换含义:比如一个线程 需要更改主物理内存的值比如主物理内存age=5; 线程1需要copy一份并修改为指定的值假如是200;此时修改完后需要写进主物理内存,写的时候需要比较原来的期望值是5 ,现在主物理内存如果是5的话就更新不是的话就不更新AutomicInteger.compareAndSet(期望值,要更改的值)

Unsafe类一些代码

比如调用自动增加的方法 getAndIncrement(){ return unsafe.getAndAddInt(本对象,地址,1) }

都会来比较内存地址的值和原来的数值如果一样就更新 有一个dowhile会一直比较

Unsafe类+cas思想(自旋锁--死循环)

CAS缺点:循环时间长 给CPU带来很大开销 引出ABA问题 只能保证共享一个变量



ABA问题

其实这个问题我想到了 提前取出内存中某时刻的数据并在当下时刻比较并替换 这个时间差会导致数据的变化。 假如 一个线程把age初始值为1 两个线程old值为一时才更改 线程一先运行然后立即转到线程2 线程2把age值改成2 然后又马上改成1 此时切换到线程一 线程一认为age 值没有修改  并且把age修改为3

原子更新引用  的概念

(原子引用)AtomicReference<User>   <> 里面是泛型 User 就一个get和set 两个 成员变量不贴了

public class AtomicRederenceMy {
    public static void main(String[] args) {
        User u=new User(1,"lsi");
        User u1=new User(2,"hah ");
        AtomicReference<User> atomicReference=new AtomicReference<>();
        atomicReference.set(u);
        System.out.println(atomicReference.compareAndSet(u,u1));
        System.out.println(atomicReference.compareAndSet(u,u1));
    }

}

解决办法:

AtomicStampedReference 带时间戳的原子引用

public static void main(String[] args) {
    Integer myinte=new Integer(100);
    AtomicStampedReference<Integer> atomicStampedReference=new AtomicStampedReference<Integer>(myinte,0);
    int stamp=atomicStampedReference.getStamp();
    new Thread(new Runnable() {
        @Override
        public void run() {

            atomicStampedReference.compareAndSet(myinte,new Integer(10),0,stamp+1);
            atomicStampedReference.compareAndSet(10,new Integer(100),atomicStampedReference.getStamp()-1,stamp+1);

        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {

            }
            boolean b=atomicStampedReference.compareAndSet(myinte,new Integer(10),0,stamp+1);
            System.out.println(atomicStampedReference.getReference()+"---"+b);
        }
    }).start();

}



未完待续...

原文地址:https://www.cnblogs.com/xuexidememeda/p/12430117.html

时间: 2024-10-07 09:32:52

你需要了解的多线程知识(JAVA ) 复习的相关文章

Java复习第三天---集合框架的相关知识

集合框架总览: Collection 接口常用方法 //1.add()向集合中添加数据 c.add(apple01); c.add(apple02); c.add(apple03); c.add(apple04); c.add(apple05); //2.isEmepty()检测当前集合是否为空 boolean empty = c.isEmpty(); System.out.println("is empty:"+empty); //3.size()返回当前集合的长度 int size

Java多线程知识小抄集(三)

本文主要整理博主遇到的Java多线程的相关知识点,适合速记,故命名为"小抄集".本文没有特别重点,每一项针对一个多线程知识做一个概要性总结,也有一些会带一点例子,习题方便理解和记忆. 51. SimpleDateFormat非线程安全 当多个线程共享一个SimpleDateFormat实例的时候,就会出现难以预料的异常. 主要原因是parse()方法使用calendar来生成返回的Date实例,而每次parse之前,都会把calendar里的相关属性清除掉.问题是这个calendar是

Java多线程知识小抄集(二)

本文主要整理博主遇到的Java多线程的相关知识点,适合速记,故命名为"小抄集".本文没有特别重点,每一项针对一个多线程知识做一个概要性总结,也有一些会带一点例子,习题方便理解和记忆. 1-26请参考<Java多线程知识小抄集(一)> 27. ConcurrentHashMap ConcurrentHashMap是线程安全的HashMap,内部采用分段锁来实现,默认初始容量为16,装载因子为0.75f,分段16,每个段的HashEntry 28. 线程安全的非阻塞队列 非阻塞

Java多线程知识小抄集(一)

本文主要整理博主遇到的Java多线程的相关知识点,适合速记,故命名为"小抄集".本文没有特别重点,每一项针对一个多线程知识做一个概要性总结,也有一些会带一点例子,习题方便理解和记忆. 1. interrupted与isInterrupted的区别 interrupted():测试当前线程是否已经是中断状态,执行后具有状态标志清除为false的功能. isInterrupted():测试线程Thread对象是否已经是中断状态,但不清楚状态标志. 方法: public static boo

JAVA多线程知识总结(二)

本文是承接上一篇文章:JAVA多线程知识总结(一) 四.Java多线程的阻塞状态与线程控制  上文已经提到线程阻塞的集中具体类型.下面主要看引起JAVA线程阻塞的方法 1,join()-----让一个线程等待另一个线程完成之后才继续执行,如A线程的执行体中调用B线程的join方法,则A线程被阻塞,知道B线程执行完成之后,A才得以继续执行 package com.bpan.spring.beans.thread; public class ThreadTestJoin { public stati

java复习预科知识-Markdown学习

java复习预科知识-Markdown学习 写在前面 1.最近深感在家学习效率低下,java基础不太稳固,因此趁放假在家,也无法走动,闲来无事,借写博客进行复习.2月份争取一天一更,每篇博客仅探讨一个小的技术点. 2.Markdown语法并不属于java语法,但是现在几乎所有的博客网站都支持该语法,关于该语法的介绍就不做介绍了,本套系列博客都是基于该语法进行编写,并发布在博客园网站内.百度上搜孤傲的咸鱼,欢迎各位读者关注. 1."标题"语法 # 表示一级标题 => h1标签 ##

多线程(三) java中线程的简单使用

============================================= 原文链接:多线程(三) java中线程的简单使用 转载请注明出处! ============================================= java中,启动线程通常是通过Thread或其子类通过调用start()方法启动. 常见使用线程有两种:实现Runnable接口和继承Thread.而继承Thread亦或使用TimerTask其底层依旧是实现了Runnabel接口.考虑到java的

java复习整理之集合

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.ios培训..Net培训</a>.期待与您交流!------ java复习整理之集合: Collection:集合中的最顶层接口,提供了一些操作集合的共性方法添加:boolean add(E e)boolean addAll(Collection<? extends E> c)删除:void

Java复习第一天---Javascript的基本知识点

1.HelloWord: 2.基础语法: 3.嵌入HTML 借助window.onload事件在整个窗体载入完毕之后运行程序代码 4.事件驱动: 5.DOM操作 5.1 查找元素节点 5.1.1 依据id值 方法:document.getElementById(id值) 获取到的是一个元素节点 var bj = document.getElementById("bj"); alert(getText(bj)); 5.1.2 依据标签名 方法:document.getElementsBy

编程基础知识——Java JNI开发流程(2)

android中使用jni调用本地C++库 android平台上的本地库文件后缀 .so.类似windows上的dll文件. 要在android上使用jni,首先需要下载android ndk. 操作步骤,正常建立android工程,然后在android工程那里右键,属性,选择Android Tools -> Add Native Support.就可以为android工程增加本地库支持. 添加支持后的android工程,会增加jni目录,C++代码就写在这个目录里. 新建一个类,并且使用nat