java并发编程中的一些理解

来源:互联网 发布:mac上打卡文件 编辑:程序博客网 时间:2024/05/17 22:45

1、java并发编程中需要注意的挑战以及应对策略?

在java并发编程中,会遇到上下文切换死锁等问题。要想减少上下文切换,主要有以下的方法:

  1. 无锁并发编程,比如将数据的ID按照Hash算法取模分段,不同的线程处理不同的段;
  2. CAS算法;
  3. 使用最少线程;避免创建不必要的线程;
  4. 协成;在单线程中实现多任务的调度,在单线程中维持多个任务间的切换。

避免死锁的一些常用手段:

  1. 避免一个线程同时获得多个锁;
  2. 避免一个线程在锁内同时占用多个资源,一般来说一个锁只占用一个资源;
  3. 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制;
  4. 对于数据库锁,加锁和解锁必须在同一个数据库连接中,否则就会出现解锁失败!

2、java并发机制的volatile和synchronized.

volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的”可见性”。
可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。
如果volatile变量修饰符使用恰当的话,他比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换和调度。

2.1 volatile的两个实现原则:

1、Lock前缀指令会引起处理器缓存回写到内存;
2、一个处理器的缓存回写到内存并导致其他处理器的缓存无效。

2.2 synchronized

在Java 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。*锁可以升级,但是不能降级,目的是为了提高获得锁和释放锁的效率*。

2.3 锁的优缺点和对比

这里写图片描述

3、原子操作的实现原理

处理器提供总线锁定缓存锁定两个机制来保证复杂内存操作的原子性。
1、使用总线锁保证原子性
所谓总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存。总线锁定把cpu和内存之间的通信锁住了,这也就是说在锁定期间,其他处理器不能操作其他内存地址的数据,所以总线开销就比较大。
2、使用缓存锁定保证原子性
所谓”缓存锁定”是指内存区域如果被缓存在处理器的缓存行中,并且在Lock操作期间被锁定,那么当它执行锁操作回写到内存时,处理器不在总线上声明LOCK#信号,而是修改内部的内存地址,并允许它的缓存一致性机制来保证操作的原子性。
缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据,当其他处理器回写已被锁定的缓存行的数据时,会使缓存行无效。
但是有两种情况下处理器不会使用缓存锁定。
第一种情况是:当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,则处理器会调用总线锁定。
第二种情况是:有些处理器不支持缓存锁定。

4、java如何实现的原子性?

通过循环CAS方式实现的。
锁机制保证了只有获得锁的线程才能操作锁定的内存区域。JVM内部实现了很多种锁机制,有偏向锁、轻量级锁和互斥锁。有意思的是除了偏向锁,JVM实现锁的方式都用了循环CAS,即当一个线程想进入同步块的时候使用循环CAS的方式来获取锁,当它退出同步块的时候使用循环CAS释放锁。

1 0
原创粉丝点击