• Java并发1


    进程

    进程是操作系统进行资源资源分配的单位,进程中包含若干线程

    线程

    线程是CPU进行调度和执行的基本单位

    方法区 堆 虚拟机栈 本地方法栈 程序计数器

    每个线程拥有自己的栈和PC

    多个线程共享方法去和堆

    并行和并发

    并行

    单位时间内,任务同时执行

    并发

    在一定时间内,任务都执行了

    并行是一起执行。并发是宏观上一起执行的,实际上是CPU时间片的轮询

    创建线程的方式

    继承Thread

    继承thread

    重写run方法

    没有返回值

    实现Runnable

    重写run方法

    没有返回值

    相对于继承thread。更新灵活,避免了单继承的局限性

    实现Callable

    实现callable接口

    重写call方法,此方法带有返回值,且有抛出异常

    thread构造器无法直接传入,需要通过Runnable的子类,futureTask进行传入

    获取数据时,使用futureTask.get()方法获取。此方法是阻塞的

    总结

    继承thread,每启动一个线程,都是一个新的资源类,不能使用内部非静态变量作为锁。实现Runnable,避免了单继承,多个线程可以共享一个资源类,在使用时,只能使用主线程被final修饰的变量。

    Thread线程只能调用一次start()。多次会抛出异常

    run()方法的调用是在start0()里面调用的是,是本地native c++方法

    线程的生命周期

    新建

    初始化状态,线程被构建,但是还没有调用start()方法

    运行

    运行状态,运行状态包含运行态和就绪态。

    阻塞

    阻塞状态,表示线程阻塞于锁

    等待

    等待状态,,表示线程进入了等待状态,进入改状态的线程需要等待其他线程通知或中断

    超时等待

    超时等待,她可以在指定时间后,自行返回

    销毁

    终止状态,表示当前线程已经执行完毕

    线程创建之后处于新建态,调用start()之后处于ready(可运行态),线程获取到了cpu时间片后处于running(运行态)。

    调用sleep方法后,线程会进入阻塞态,sleep结束后,恢复到ready,等待cpu时间片的调用

    一个java程序启动,至少包含2个线程 ,一个main线程,一个gc线程

    守护线程

    //设置为守护线程
    setDaemon(true);
    如果用户线程结束了,守护线程也会结束
    

    join方法

    join方法底层是wait()方法,调用join方法后,调用者就会被阻塞。直到线程运行完后,调用者所在的线程才会继续执行

    yield方法

    使当前线程由运行态--进入到就绪态 (Ready)。交互cpu时间片

    并发编程中的三个问题

    可见性

    可加性:是指一个线程对共享变量进行修改,其他线程可立即得到修改后的值

    案例:

    假设新开一个线程,线程里面操作资源类的内容变更,主线程采用while循环判断内容是否更改(采用while可以避免虚假唤醒)。

    未加volatile关键字修饰变量。则会一直循环,因为其他线程无法感知到内容实际已修改,即未从工作内存同步到主存中。

    使用volatile关键字后,如果变量被线程修改,其他线程可感知到,则案例死循环将终止。

    volatile可以保证内存可见性,不能保证原子性。可以防止指令重排

    原子性

    原子性:在一次或多次操作中,要么操作全部成功,要么全部失败。不会存在中间状态。

    解决原子性方案:synchronized 、 cas原子类工具、lock锁机制

    synchronized 、AtomicInteger、【lock.lock、lock.unlock】

    有序性

    有序性:指的是程序中代码的执行顺序,Java在编译和运行时会对代码进行优化(指令重排)来加快程序的速度,但是这样会导致程序的执行顺序和我们编写的代码顺序不同。在并发下,就会产生问题。

    初始化一个对象
    instance=new Demo();  是被分为三部分
    memory=allocate(); 分配内存空间   步骤1
    instance(memory) 初始化对象       步骤2
    instance=memory; 设置instance指向刚刚分配的内存空间地址。此时instance!=null   步骤3
    
    

    步骤2和3不存在依赖关系,是否发送重排,单线程下,结果都一样。但是如果步骤3在步骤2前,这个时候instance不为null,但实际初始化工作还没有完成。就会返回一个null的getInstance。这时候数据就出现了问题。

    解决方案,加volatile,防止指令重排

    CAS

    • CAS:cas是比较并交换compareAndSet,是一条CPU并发原语,功能是判断内存某个位置的值是否为预期值,如果是则更新为新的值,这个过程是原子性的。

    • 采用Cas+volatile可以保证内存可见性、防止指令重排、以及原子性。解决了并发问题。

    • cas底层原理:

    • 调用Unsafe类中的Cas方法,jvm帮我们是先出cas汇编指令,它是依赖于硬件的功能,通过她实现原子操作,Cas是CPU的一条原子指令。

    • cas的思想就是乐观锁的思想.。java中,采用while循环进行自旋,尝试获取锁或者满足期望值。

    CAS三大问题

    • 如果cas长时间不成功,会给cpu带来开销,java中,是采用while进行一直循环的
    • 只能保证一个共享变量的原子性
    • 存在ABA问题

    ABA问题

    ABA问题指的就是,初始化值为A,如果线程1修改A为B。然后线程2修改A值为C。但是线程1修改成功后,又将B修改为了A。此时,线程2去修改,发现原来的值还是A,她就认为数据没有被修改过,然后就将A修改为了C。但是实际情况,A是被修改过后的,并不是原来默认的A了。

    解决方案

    增加版本号。通过版本号机制,每次修改,都将版本号+1,修改时,判断版本号是否和自己获取前的一致,一致则修改,否则修改失败。

    java juc下的AtomicStampedReference类就是ABA的一种解决方案。

    只能保证一个共享变量的原子性

    java中采用AtomicReference、AtomicStampedReference 原子引用来保证引用对象之间的原子性

    Synchronized优化

    synchronized可以同时保证可见性,有序性,原子性

    锁消除

    锁消除是JIT编译器对synchronized做的优化,在编译的时候,jit通过逃逸分析技术,来分析synchronized锁对象,判断她是不是只可能被一个线程来加锁,没有其他线程来竞争加锁,这个时候编译就不加入monitorenter和monitorexit的指令。仅仅一个线程争用锁的时候,就可以消除这个锁了,提示了代码的执行效率,因为只有一个线程来加锁,就不会涉及到锁的竞争

    锁粗化

    synchronized(this) { 
    
    }
    synchronized(this) {
    
    }
    synchronized(this) {
    
    }
    ----------------------------------------------------
       最终会优化成一个
       synchronized(this) {
    
    }
    

    锁粗化的意思是,如果JIT编译器发现了代码中连续多次加锁释放锁的代码,会结合成一个锁,这就是锁粗化,避免多次加锁释放锁的开销

    偏向锁(我偏心)

    monitorenter和monitorexit是要使用CAS操作加锁和释放锁的,开销较大。因此,如果发现大概率只有一个线程会主要竞争一个锁,那么就会给这个锁一个偏好,后面她加锁和释放锁,都基于偏好来执行,不需要通过cas,性能会有提升。但是如果有偏好之外的线程来竞争锁,就会回收之前的偏好,但是其他线程来竞争锁的记录交小。

    轻量级锁

    如果偏向锁没有成功,说明锁的竞争很激烈。那么这个时候就会尝试采用轻量级锁来加锁,就是将对象头的MarkWord里面有一个轻量级锁的指针,尝试指向持有锁的线程,然后判断一下是不是自己加的锁,如果是自己加的锁,那就指向代码。如果不是自己加的锁,那就是加锁失败,说明以及有其他人加锁了,这个时候就会升级为重量级锁

    适应性锁

    JIT编译器的优化,如果每个线程持有锁的时间非常短,那么一个线程获取不到锁,就会暂停,发生上下文切换,让其他线程来执行,但其他线程很快就释放了锁,然后唤醒暂停的线程,加入锁的竞争,这样线程会频繁的上下文切换,导致开销过大。针对这种情况,可以采用忙等策略,线程没有获取到锁,就进入while循环不停等待,不会暂停发生上下文切换,等到机会获取到锁继续执行就好了。

  • 相关阅读:
    LightOJ1002 分类: 比赛 最短路 2015-08-08 15:57 15人阅读 评论(0) 收藏
    DZY Loves Chemistry 分类: CF 比赛 图论 2015-08-08 15:51 3人阅读 评论(0) 收藏
    周赛-DZY Loves Chessboard 分类: 比赛 搜索 2015-08-08 15:48 4人阅读 评论(0) 收藏
    周赛-Equidistant String 分类: 比赛 2015-08-08 15:44 6人阅读 评论(0) 收藏
    周赛-Toy Cars 分类: 比赛 2015-08-08 15:41 5人阅读 评论(0) 收藏
    A Knight's Journey 分类: POJ 搜索 2015-08-08 07:32 2人阅读 评论(0) 收藏
    哈希-4 Values whose Sum is 0 分类: POJ 哈希 2015-08-07 09:51 3人阅读 评论(0) 收藏
    哈希-Gold Balanced Lineup 分类: POJ 哈希 2015-08-07 09:04 2人阅读 评论(0) 收藏
    哈希-Snowflake Snow Snowflakes 分类: POJ 哈希 2015-08-06 20:53 2人阅读 评论(0) 收藏
    欧拉通路-Play on Words 分类: POJ 图论 2015-08-06 19:13 4人阅读 评论(0) 收藏
  • 原文地址:https://www.cnblogs.com/chaoba/p/15970021.html
Copyright © 2020-2023  润新知