首页 > 编程语言 >Java并发

Java并发

时间:2022-10-14 00:00:07浏览次数:61  
标签:int Java CAS 阻塞 并发 读锁 线程 final

final关键字

  • 父类的私有方法是默认为final的,因此无法继承和重写,如果子类中还有则是新成员。
  • 基本类型的final域重排序规则:如
public class FinalDemo{
    private int a;//普通域
    private final FinalDemo b;//final域
}

重排序会保证在赋值之后再进行引用。

  • 内存屏障解决的问题:多线程条件下缓存同一个变量会出现问题。
  • 内存屏障的作用:volatile就通过内存屏障来解决这个问题,java的内存屏障有4种,LoadLoad,StoreStore,LoadStore,StoreLoad详情

CAS

  • 基于硬件实现原子地更新某个位置的值,基于锁的实现一般来说开销比较大,比如使用synchronized作为关键字:
    public synchronized int add(){
        return i++;
    }

CAS不加锁实现一致性:

    private  AtomicInteger i = new AtomicInteger(0);
    public int add(){
        return i.addAndGet(1);
    }

CAS为乐观锁,而Synchroinized为悲观锁,CAS有以下问题:

  • ABA问题:在CAS进行操作值的时候,检测值有无变化,如果是A->B->A,会认为值没变.

    解决方法:

    1.版本号,记为1A->2B->3A.

    2.使用jdk中的AtomicStampedReference来解决,使用compareAndSet方法
  • 循环时间长开销大:自旋CAS长时间不成功,会有大开销,需要pause指令,作用为:1.延迟流水线执行命令 2.避免退出循环时内存顺序冲突导致流水线被清空导致CPU流水线被清空.
  • 只能保证一个共享变量原子操作,可以通过把多个共享变量合成一个来实现,java使用AtomicReference类。

AtomicInteger底层实现:

  • CAS+volatile,使用volatile保证线程可见,多线程并发时使用CAS保证更新后的原子性。

JUC锁

连接

  • 由主线程wait,然后由子线程执行后notify返回主线程,实现同步的过程。
  • 另一个方法使用park和unpark进行加锁和释放锁,进行同步,使用getBlocker可以看到加的锁名,使用这种方法不会导致顺序不当的阻塞,更加灵活,上面的会。

阻塞的区别

1.Thread.sleep()和Object的区别在前者不会释放锁资源,后者会释放锁资源。

2.Object.wait()和Condition.await()区别在Condition.await()底层使用LockSupport.park()来实现阻塞当前线程,在阻塞前还把当前线程加到条件队列,让state变为0之后再调用LockSupport.park()阻塞当前线程。

AQS

请求共享资源空闲时,将共享资源设为锁定态,如果被占用,就使用CLH队列锁将暂时拿不到锁的线程加到队列里。

连接

isHeldExclusively()//该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int)//独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。

AQS定义了两种资源共享方式:1.独占(分为公平锁和非公平锁)2.共享

  • 可重入:强调一个程序运行被中断然后调度另一段代码,该代码再回来执行被中断程序时,不会出错。
  • 可重入锁是递归锁,就是外层拿到锁,继续向下执行不会被阻塞,而是仍然处于拿到锁的状态。

ReentrantLock和ReentrantReadWriteLock的内部实现
链接
对于ReentrantReadWriteLock内部,高16为读锁,低16为写锁,最大数量各为2的16次方-1。

锁的升级

当一个线程拥有一个读锁,此时该线程想要获取写锁来做一些事情,但是又不想释放手上的读锁,否则下次再获取读锁时就要排队,注意读锁不能升级为写锁,因为容易造成死锁。

  • 举例:如果两个线程拿着读锁,然后都想升为写锁,则必须消耗读锁,此时互相等待。

自旋锁

让当前线程等一下,等前面锁定同步资源的线程释放锁,就不必阻塞直接获取,避免切换线程开销,阻塞锁则完全相反。

JUC集合类

  • Hashtable慢的原因是使用synchronized对put操作加锁,对整个对象加锁.
  • ConcurrentHashMap在1.7版本中的分段锁机制:

链接
segment数为16,最多支持16个线程并发,计算key的hash值,根据hash值找到Segment数组位置j,初始化后将值插入到槽s中。

标签:int,Java,CAS,阻塞,并发,读锁,线程,final
From: https://www.cnblogs.com/mengyiqwq/p/16790156.html

相关文章