java锁分类


为了保证并发编程安全,java提供了各种各样的锁,将并发操作通过逻辑转化为顺序执行,了解锁的分类有助于从整体上对java锁理解。

1 线程要不要锁住同步资源:乐观锁、悲观锁

在这里插入图片描述

2 根据加锁粒度:类级锁、对象锁、分段锁、单个变量/字段加锁

2.1 类级锁

当一个线程获得一个该类创建的对象锁时,其它该对象实例都不能被其它线程再加锁。
实现方式:
1)使用synchronized修饰类的静态成员方法
2)使用synchronized修饰类中静态代码块,括号使用ClassName.class对类加锁。

public class Taxi{
		public synchronized static void methodA(){//doSomething}
		public void methodB{synchronized(TestClass.class){//doSomething}}
}

在这里插入图片描述

2.2 对象锁

一个对象创建一把锁,多个线程可以同时对一个类的多个实例对象同时加锁,彼此之间互不冲突。
实现方式:
1)使用synchronize修饰类的实例方法。
2)使用synchronized修饰类的代码块,括号中填写this对当前实例对象加锁。

public class Taxi{
		public synchronized void methodA(){//doSomething}
		public void methodB{synchronized(this){//doSomething}}
}

在这里插入图片描述

2.3 分段锁

对一个类中部门操作使用同步代码块,多个同步代码块之间彼此互不影响。
实现方式:
1)通过synchronized修饰代码块实现。
2) 通过lock实现

public class Taxi{
	    private Driver driver;
			private Car car;
			public void methodA(){ synchronized(driver){//doSomething}}
			public void methodB{synchronized(car){//doSomething}}
}
public class Taxi{
      public void methodA{
          Lock lock = new ReentrantLock();
          lock.lock();
          try{
          }finally(){
           lock.unlock();
           }
      }
}

在这里插入图片描述
典型代表ConcurrentHashMap分段锁
jdk1.7 ConcurrentHashMap数据结构:
在这里插入图片描述
jdk7 元素更新加锁过程:
1 根据key值的hash值确定元素锁在段,同时对获得该段的锁,使用ReentrantLock对HashEntry数组进行加锁;
2 再次哈希确定元素所在链表。
3 通过循环遍历链表,确定元素位置,更新元素。
4 释放段的锁
总结:jdk1.7中锁是通过segent+hashEntry+ReentrantLock

JDK 8 ConcurrentHashMap数据结构:
在这里插入图片描述
1 计算key的哈希值确定要插入数组中的节点位置。
2 节点位置有四种情况:
2.1 table还没有创建,先初始化数据table。
2.2 节点为空,采用cas更新值。
2.3 节点正在迁移,走迁移插入流程。
2.4 先使用sychronized对第一个节点加锁,如果第一个节点keyhash值大于等于0,是单链表,按照单链表查找更新;否则,为红黑树,按照红黑树方式查找更新。
3 更新完之如果是对单链表中插入,判断单链表长度大于8,转化为红黑树,同时这个map中元素个数大于64。
小结:jdk 1.8使用了Node+cas + synchronized实现了加锁。

2.4 单个变量/字段加锁

使用CAS和java中原子引用类,通过保证编发编程中原子性,实现数据安全同步更新。

3 根据所的兼容性:共享锁、排他锁

在这里插入图片描述

4 多个资源竞争时要不要排队:排队-公平锁,不排队-非公平锁

在这里插入图片描述

5 一个线程中多个流程能不能获得同一把锁:重入锁和不可重入锁

重入锁:同一个线程多次进入同一把锁。当一个操作要分多个部分执行,不同部分访问了了同一份临界资源。
在这里插入图片描述
ReentrantLock使用:

class ReenTrantLockDemo{
    private int counter = 0;
    private Lock  lock = new ReentrantLock();
    public int getCounter() {
        return counter;
    }
    public  void  method1(){
            lock.lock();
            try{
                method2();
                counter++;
            }finally {
                lock.unlock();
            }
    }
    public void method2(){
        lock.lock();
        try{
            counter++;
        }finally {
            lock.unlock();
        }
    }
}

6 锁同步资源失败是否需要阻塞:不阻塞-自旋锁,阻塞-阻塞锁

自旋锁使用
如果持有锁的线程能在短时间内释放锁,则线程不用在用户态到内核态进行切换。加锁操作执行时间很长的时候,不建议使用自旋锁。

class SpinLockDemo{
    AtomicBoolean available = new AtomicBoolean(false );
    public void setLock(){
       while(!tryLock()){}
    }
    public boolean tryLock(){
            return available.compareAndSet(false,true);
    }
    public void unlock(){
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值