多线程和Synchronized在其中的使用

多线程和Synchronized在其中的使用

一、多线程

1.适用场景

需要提高任务执行效率,有多个任务且任务量大,或者多个任务中有会阻塞的情况

2.线程状态

1. 新建状态(New): 线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread()。

2. 就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start()。处于就绪状态的线程,随时可能被CPU调度执行。

3. 运行状态(Running): 线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。

4. 阻塞状态(Blocked): 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

5. 死亡状态(Dead): 线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

3.线程安全问题

原因:
多个线程对共享资源进行操作

根源:不满足原子性 可见性 有序性

要做到保证安全的前提下,提高效率

具体方式:没有安全问题的代码,不需要加锁

读写分离:

​ 读 :valatile 保证可见性就够了

​ 写:加锁

4.java创建多线程的方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口

二、Synchronized

1. synchronized实现原理

synchronized(Object o){

}

本质上是线程执行到共享资源的synchronized代码块时,对o对象进行上锁,此时再有其他线程来时,发现o对象已经上锁,所以被阻塞。

对象锁机制

private static Object obj = new Object();

public static void main(String[] args) {

synchronized (obj) {

System.out.println("hello world");

}

}

以上代码进行javap -c 反编译后
在这里插入图片描述

可以看到第5行,第15行和第21行的monitorenter和monitorexit指令。

也就是说执行synchronized包裹的同步代码块的底层是:首先执行monitorenter指令,执行完代码后退出时在实行monitorexit指令。

关键就是必须对对象的monitor监视器进行获取,当线程获取monitor后才能继续往下执行,否则只能等待,而这个获取的过程是互斥的同一时间只能有一个线程能获取到monitor

以上有两个monitorexit是因为要确保异常时也能解锁

2. synchronized优化

使用monitor监视器的操作,确实能保证线程安全,但效率低下,我们在解决线程安全问题时,要在安全的前提下,尽可能的保证效率。

1. 前置知识
1. CAS

CAS(compare and swap),是java对于乐观锁的一种具体实现,基于Unsafe来实现呢,本质上是基于CPU提供的接口保证线程安全修改变量。

乐观锁:线程直接尝试对共享变量进行操作。

悲观锁:线程先加锁,然后再对共享变量进行操作。

操作过程:简单理解为CAS(V,O,N) 保存的三个值分别为:V:内存中存放是实际值;O:预期的旧值;N:更新的新值。 当V和O相同时,表明该值没有被其他线程改过,就可以将新值N赋给V;反之,V和O不相同,表示O已经不是最新的旧值了,所以不能将N赋给V。其结果就是当多线程操作时,一个会成功,其他的会失败。

自旋锁:在多线程情况下,某一线程CAS操作失败,使用自旋方式,去不停的进行CAS访问。

ABA问题: CAS在检查旧值有没有变化的时候,如果这个旧值A被其他线程修改成了B,又被修改成了A,这样发现旧值没有变化,实际上已被其他线程修改过。 解决的方案就是采用数据库中常用的乐观锁方式,添加一个版本号就可以解决,

公平性:自旋锁的问题是处于自旋状态的线程,更有可能优先得到这把锁。

2. java对象头

Synchronized获取到的对象的锁实际上是对象头上的一个标志,存储在对象头中的Mark Word里。

2. 锁升级过程
  1. **起初是无锁状态,当某一个线程访问同步代码块并获取锁时,升级为偏向锁。**会在对象头和栈帧中的锁记录里存储偏向的线程ID,之后如果是同个线程再次进入同步代码块就不再需要进行CAS操作来加锁。
  2. 当有其他线程尝试竞争偏向锁时,就会升级为轻量级锁,JVM首先会在当前线程的栈帧中创建用于存储锁记录的缓存空间,并将对象头的Mark Word复制到锁记录中,然后当各个线程尝试对 对象头中的Mark Word进行CAS操作,如果成功,该线程获得锁,如果失败,代表其他线程也在竞争,该线程就会自旋来不停的获取锁。轻量级锁只要为了在 多线程在不同的时间段请求同一把锁这种情况下提高效率,来避免线程的阻塞以及唤醒。
  3. **当多线程同时竞争锁资源时,就会升级为重量级锁。**JVM虚拟机会阻塞加锁失败的线程,并且在目标锁 。Java线程的阻塞以及唤醒,都是依靠操作系统来完成的,需要进行用户态和内核态的切换,所以非常影响性能。而升级为重量级锁之后,再也无法退回轻量级,所以为了尽量避免这种多余的阻塞,唤醒操作。在线程进入阻塞状态之前,以及被唤醒之后竞争不到 锁的情况下,进入自旋状态,在处理器上空跑并且轮询锁是否被释放。如果此时锁恰好被释放了,那么当前线程便无须进入阻塞状态,而是直接获得这把锁。
3. 其他的优化
  • 锁粗化

    当连接在一起的多次加锁,解锁过程存在时,会将其合并为一个范围更大的锁。

  • 锁消除

    当检测到一段代码中,堆上的数据不会逃逸出 当前线程,那么可以认为这段代码是线程安全的,不必要加锁。

死锁问题
  • 锁粗化

    当连接在一起的多次加锁,解锁过程存在时,会将其合并为一个范围更大的锁。

  • 锁消除

    当检测到一段代码中,堆上的数据不会逃逸出 当前线程,那么可以认为这段代码是线程安全的,不必要加锁。

死锁问题

描述:不同的线程分别占用对方需要的同步资源,都在等待对方放弃自己需要的同步资源,就会出现死锁。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值