多线程的使用-->2

1.线程同步

当多线程执行,对于一个共享变量进行操作时,会产生一些安全问题,称为同步问题。

线程同步就是在多线程访问共享变量时,通过一定的机制,使得线程可以按照一定的顺序,每次只能有一个线程访问共享变量。

1.1解决线程同步

给程序枷锁     两种:

①.悲观锁:必须要上锁,有具体的上锁操作,悲观锁有两种,使用synchronized关键字,使用Lock对象

②.乐观锁:不是真正意义上的锁,没有具体的锁操作,但有一个数据状态。每次操作数据前,会获得数据状态。在操作数据时,如果发现状态发生了变化,基于新状态重新操作 ​ JDK针对于数字计算,提供了原子类,底层使用的是CAS + 自旋机制

( synchronized )锁的使用该关键字可以给方法上锁,也可以给代码段上锁

  • 当线程调用同步方法或同步代码段时,就会获得一个对象锁

    每一个对象创建时,都会有一个对象锁,本质是一个监视器Mointer

  • 当synchronized关键字修饰的是方法时,调用该同步方法获得的是该方法所属对象的对象锁

    而不是调用这个方法的线程对象的对象锁

    当线程争抢对象锁时,如果对象所以被占用,线程将处于等待状态

    当另一个线程执行完毕,释放了占有的锁, 这些等待的线程才能继续执行。

  • 线程进入等待状态,本质就是加入了一个同步等待队列(是系统级别的)

方法

同步代码段:同步代码段所需要的锁,是通过传参指定的。只要保证传递对象是唯一的即可。

Lock锁的使用:Lock本身是一个接口,我们实际应用时使用其对应的子类ReentrantLock

Lock的使用过程

  1. 创建锁对象。 如果多个线程需要争抢一把锁,就创建一个锁对象。 需要多个锁,就创建多个锁对象

  2. 调用lock对象的lock()尝试争抢锁。 如果抢不到锁,线程进入等待状态。

    有一个从在方法tryLock(10,TimeUnit.SECONDS)尝试在指定的时间内获得锁。返回boolean

  3. 调用lock对象的unlock(),释放锁。 其他等待锁的线程才能继续争抢。

在使用lock锁,建议将获得锁的代码写在try中,将释放锁的代码写在finally中

确保无论操作是否成功还是是否,都能释放锁。

Lock锁的底层机制

底层使用的是CAS + AQS

  1. 在lock底层有一个计数器,记录锁被获取的状态,起初为0 , 当被抢占的时候变为1

    也就是说,当我们调用lock.lock()方法,就是将状态从0改为1的过程

    当我们调用lock,unlock()方法时,就是将状态从1改为0的过程

    当我们调用lock.lock方法时,如果发现状态是1,表示锁被占用,当前线程进入等待状态

  2. 当多个线程同时访问lock.lock()方法时,就想将状态从0改为1

    每个线程都尝试着将状态从0改为1. 假设a线程最先完成状态改变,a线程获得了锁

    此时b线程也尝试将0改为1,先获得原始值0,将0改为1,再将改好的1替换原始值0(赋值)

    在替换前,会拿着之前的原始值与现在变量里的值进行比较,看看是否发生了变化

    如果没有变化,说明这个过程中没有其他线程访问资源,将其改为1获得锁。

    如结发生了变化,说明这个过程中被其他线程捷足先登了,其他的线程获得锁,当前线程等待

    我们称这个过程为 CAS (compare and set)

  3. 当一个线程获得锁时,发现锁已经被占用了,当前这个线程就会处于等待状态

    实际上,并不是线程对象有一个状态码,改为等待状态的值。

    实际上是将需要等待的线程,存入了一个集合,并使其进入最终等待状态(jvm级别的等待状态)

    当最开始线程执行完毕释放锁后,就会从这个集合中取出最开始的那个线程继续执行

    这个集合我们就称为 AQS (抽象的)队列同步器

乐观锁的使用:乐观锁不是一种真正存在的锁,而是一种机制。  底层使用的是 CAS + 自旋 应用组合

  • 乐观锁的使用更具有局限性, 适用于数字变量的计算,一般多是 ++ 和 --

  • JDK中提供了一个原子类 AtomicIntger,该类对象中提供了++和--的计算方法

    当通过该类对象提供的++和--的方法计算时,可以确保线程同步。

AtomicInteger的常用方法

CAS特点是,在set设置新值之前,先用原始值与当前变量中的值做比较,相等说明这个过程中没有其他线程操作变量,也就相当于当前线程占有变量。不相等说明这个过程中有其他线程操作变量,也就是这个变量其他线程占有,我需要重新获得。

ABA问题:在比较时原始值与当前变量中的值相等,不能说明这个过程中变量没有被其他线程操作,因为有可能另一个线程将变量中的值,从A改成了B又改回成了A。

解决ABA:可以为数据增加一个版本号,只要改变过,版本号就+1

JDK提供了一个可以解决ABA问题的原子类

  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值