Java多线程详解(二)

发布于:2022-12-07 ⋅ 阅读:(702) ⋅ 点赞:(0)

多线程并发处理

一、线程安全

在代码中要保证各个线程的安全,要满足线程三大多线程的并发特性,即原子性,内存可见性和避免指令重排序。

一、原子性

原子性是指一些列的操作是连贯的,不可分割的。

二、内存可见性

要求是在多线程的情况下,每一个线程都可以看见其他线程对一个它所关注的共享变量的操作,这个共享变量一旦发生改变,它能立即刷新看到最新的值,这些操作都是在内存中进行的,这就是多线程的内存可见性。可以将内存可见性扩展理解成内存共享变量值的立即刷新可见性,内存可见性的重点,就是多线程中的某个被标记的共享变量值一旦改变,其他线程能立即刷新得到最新值。

三、避免指令重排序

程序在运行过程中,Java 虚拟机实际上会对编写的代码进行一定的优化。特别是 Java 虚拟机为了提高多线程的并发能力,有时需要对 Java 代码的内部指令进行重新排序后执行,称为指令重排序。
在多线程的情况下,这样的指令重排序优化可能会导致线程的不安全,特别是多个线程数据读写操作并存的情况下,会有较大概率出现数据的错误读取。

在Java中,关键字syncchronized和volatile可以做到内存可见性和屏蔽指令重排序。

二、线程同步

在多线程的领域中,同步是一个多线程并发的情况下的特殊需求,即同步首先要求在多线程并发的情况下才能发生。多线程同步的要求:当一个线程对某内存块 A 进行操作时,若其他线程同时也需要对内存块 A 进行操作,则需要等待前一个线程操作完成后才可以进行。
同步的“同”,并不是指同时进行,而更多是等待的含义,类似于有共“同”的需要而等待。

一、synchronized关键字

可以作用在以下三个地方:
某方法中的一段逻辑代码块,某一方法,某一静态方法。

  • 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号 {}括起来的代码,作用的对象是调用这个代码块的对象;调用synchronized 代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。
  • 修饰某个普通方法,则这个方法的同步监控锁是该类的实例化后的该对象级别的,即一个类实例化了多个对象实例,则会产生多个同步监控锁,对应着每一个对象实例。这样的对象级别的同步监控锁,能监控该对象中的所有使用了
    synchronized修饰的普通方法,能保证同一时间只有一个这样的方法被一个线程调用。 使用 synchronized
    修饰的普通方法可以转换为如下同步代码块:
synchronized(this){
……//省略部分代码
}

也就是说,这里的同步监控锁是每一个对象自身。 图解如下:
对象级同步监控锁图

  • 修饰一个类的静态方法则这个方法的同步监控锁是整个类的级别,即一个类无论实例化了多少个对象实例,它们都用同一把同步监控锁,因为这些对象都归为这个对象的实例。受类级别同步监控锁的监控。
    这样的类级别的同步监控锁能监控该类的所有新建出来的对象实例中的所有使用了 synchronized
    修饰的静态方法,能保证同一时间只有一个这样的方法被一个线程调用。

    使用 synchronized 修饰的静态方法可以转换为如下同步代码块:

synchronized(XXX.class){
  ……//省略部分代码
}

也就是说,这里的同步监控锁是整个类本身,而并非实例化的对象。 图解如下:
类级同步监控锁

二、volatile关键字

在多线程中被volatile定义的变量,一旦有变化,要即时做出通知。
volatile可以看作synchronized的轻量版本,但是volatile无法满足原子性,所以在多线程并发的情况下,尽量试用synchronized关键字。

三、多线程的同步锁机制

java 中的同步监控锁有以下三种分级:重量级锁、轻量级锁和偏向锁。其中,synchronized同步关键字所使用到的同步监控锁就是一种重量级锁,这样的重量级锁对系统的资源消耗相对较大。

一、多线程的死锁和活锁

在多线程的情况下,如果并发中的一个线程对某些共同的资源需要持续占用使用,但资源的分配不足,就可能会导致死锁的情况出现。而活锁不同,若一个线程无法一次获得全部的资源,则它会马上释放所有的资源然后等待下一次机会再次获得全部的资源。
由于在同步操作中每个资源都会上一把同步监控锁,所以可以先用同步监控锁来替代资源的占用。

二、多线程的悲观锁和乐观锁

悲观锁:总是以最悲观的心态,为每一次数据的修改都上锁,这样能提高多线程下数据修改的安全性,但同时也会因频繁上锁而导致数据处理的效率降低。实际上,同步关键字synchronized 就是悲观锁的一种实现方式。
乐观锁:总是以最乐观的心态,让多线程并发的处理如常进行,只在最后一刻才进行检测,查看被锁的数据是否发生数据冲突。乐观锁能有效地提高非频繁的数据处理的效率,但如果在非常大量的线程并发下,则有可能导致数据处理的效率降低。

四、多线程的异步

在多线程的领域内,异步是指在多线程的并发合作中,每个线程都负责某个特定的环节,并且处于松耦合结构。这些线程都是并行且独立,能够互相不影响各自的计算和处理。
方法或模块的异步调用,是指当某个方法或模块被调用时,调用方无须等待该方法和模块运行完毕后的结果,而是继续后面的操作。

五、多线程的同步时序图和异步时序图

一、同步时序图

同步时序图

二、异步时序图

异步时序图

本文含有隐藏内容,请 开通VIP 后查看