java并发编程(中)

死锁

- synchronized锁机制
  - 同步机制的关键字 synchronized,当我们使用了同步后,线程A占用了该共享资源,就会马上组织其他线程对共享资源的写方法,此时共享资源的使用权仅仅属于线程A,这样就可以避免线程安全问题
  - **synchronized可以所得东西?锁的是什么?**
    - synchronized锁方法:
      - synchronized修饰静态方法:锁定的是该方法
      - synchronized修饰非静态方法:锁定的是方法的调用者
    - synchronized锁代码块
      - 锁的是当前传入的对象,同时修饰代码块时,需要this关键字,表示的就是谁获取该对象,就对谁上锁
  - 当被synchronized修饰的代码执行完成之后,会立即释放,允许别人访问
  - sleep()和wait()
    - 前者会让出CPU但是不会释放锁,后者相反,不会让出CPU但是会释放锁
    - sleep() 方法是 Thread 类中的方法,而 wait() 方法是 Object 类中的方法
    - sleep() 方法不会释放 lock,但是 wait() 方法会释放,而且会加入到等待队列中
    - sleep() 方法不依赖于同步器 synchronized(),但是 wait() 方法 需要依赖 synchronized 关键字
    - 线程调用 sleep() 之后不需要被唤醒(休眠时开始阻塞,线程的监控状态依然保持着,当指定的休眠时间到了就会自动恢复运行状态),但是 wait() 方法需要被重新唤醒(不指定时间需要被别人中断)
- 避免死锁:
  - 避免一个线程同时获取多个锁。 
  - 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。 
  - 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。 
  - 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

Java并发机制底层的实现原理

其实,所有并发操作,要想不出现问题,都需要实现**写后读操作**,即

​        **一个线程想要改变某个变量,一定是要基于其他线程的有效修改之后再去读取,读取到优秀修改之后,自己再去进行修改**。(所有线程都要遵守这一条)

​        **锁(适合高并发)、CAS(Compare and Swap)(适合低并发)机制**、**设置一个中介(每次想要修改时都会先访问中介查看资源是否修改过)(适合线程间并发、进程间并发、服务期间并发)**就是保证了上述的写后读操作.

CAS机制(Compare and Swap)

CAS 全称为Compare And Swap ,翻译过来就是**比较并且交换**,它并没有加锁,但是可以保证读后写

- Synchronized 是**悲观锁**,线程一旦得到锁,其他的线程就只能挂起了
  - 悲观锁:是真正的上锁,总是**假设最坏**的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁
- CAS的操作则是**乐观锁**,他认为自己一定会拿到锁,所以他会一直尝试,直到成功拿到为止;
  - 乐观锁:不是真正的上锁,总是**假设最好**的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁
- 机制
  - CAS 里面至少包含了两个动作,分别是比较和交换
  - CAS操作是**原子操作**,要么全部完成,要么全部没有完成(由此保证了读后写操作)
    - 因为是原子操作,所以我们无法自己编写实现,需要有操作系统帮助实现(CPU中的CAH指令)
- 步骤(例如 a + 1 的操作,a 默认=0):
  - 在多个线程修改一个值 a 的时候,会将 a copy 一份到自己的线程内存空间中(预期值),此时预期值就是 a ,要修改的值就是 a+1 的结果,结果就是 1(要修改的值),由于是多个线程,所以每个线程内部都会得到 a  = 1。
  - 接着就会执行**比较并且交换**,  让线程中的预期值和主内存中的 a 进行比较,如果相同,就会提交上去,如果不相同,说明 a 的值已经被别的线程修改过了,所以就会提交失败(这个比较和提交的操作是原子性的)。提交失败之后,线程就会重新获取 a  的值,然后重复这一操作。这种重复操作的方式称为自旋
- 优点:在**低并发**时,效率会更高
  - 原因:缺少了**上锁加锁**的性能开销
- 缺点:
  - 不适合高并发的线程
  - 原因:因为CAS并不会对资源进行上锁,所以当一个线程对其修改之后,其他线程进行比较,发现预期值与实际值不符,就会重新进行计算,这个过程需要耗费CPU资源但是却是无用功,所以并不适合高并发
- 另外,实现锁(头部加锁)也需要CAS
  - Java头对象的MarkWord共64bit,上锁需要32bit,因为是多个线程想要上锁,那就会有并发问题,所以就要使用CAS来实现读后写,如下图所示

- - CAS在轻量级锁中是**一直自旋**,在重量级锁中只**自旋一次**,然后进入堵塞队列
- 自旋CAS实现的基本 思路就是循环进行CAS操作直到成功为止,如下图

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值