cas代码以及aqs和各种锁的概念

没有任何加锁的代码进行测试

package cn.yun.basic.thread.wait;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class CasDemo {
    /**
     * @Author longhua.shang1
     * @Description 卖票操作,100张票卖个2000个人,分别没有任何锁,以及加锁状态下的各种实现
     * @Date 10:12 2021/6/18
     **/

    private static int count = 10;

    public static void main(String[] args) {
        scrambleTicketWithNoLock();
    }

    /**
     * @Author longhua.shang1
     * @Description //TODO 10:14
     * @Date 10:14 2021/6/18
     **/

    public static void scrambleTicketWithNoLock() {
        for (int i = 0; i < 100; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    getTicket();
                }
            };
            Thread thread = new Thread(runnable);
            thread.start();
        }
    }

    public static void getTicket() {
        if (count > 0) {
            System.out.println(Thread.currentThread().getName() + "获得了票" + count);
            count--;
        }
    }

}

输出的结果可能为:

Thread-0获得了票10
Thread-6获得了票10
Thread-4获得了票10
Thread-2获得了票10
Thread-3获得了票10
Thread-1获得了票10
Thread-5获得了票7
Thread-7获得了票8
Thread-11获得了票4
Thread-9获得了票1

 

public class MyStudy {
    public static int data = 0; 
    public static void main(String[] args) throws Exception{
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe = (Unsafe) theUnsafe.get(null);
        long dataAddress = unsafe.staticFieldOffset(MyStudy.class.getDeclaredField("data"));
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int j = 0; j < 100; j++) {
                        int anInt = unsafe.getInt(MyStudy.class, dataAddress);
                        while(!unsafe.compareAndSwapInt(MyStudy.class,dataAddress,anInt,anInt+1))
                            anInt = unsafe.getInt(MyStudy.class, dataAddress);
                    }
                }
            }).start();
        }
        Thread.sleep(1000);
        System.out.println(data);
    }

}

二. java中的一些锁的分类:

1.公平锁 / 非公平锁

2.可重入锁 / 不可重入锁

3.独享锁 / 共享锁

4.互斥锁 / 读写锁

5.乐观锁 / 悲观锁

6.分段锁

7.偏向锁 / 轻量级锁 / 重量级锁

8.自旋锁

三. 介绍:

  公平锁是指多个线程按照申请锁的顺序来获取锁,公平锁如ReentrantLock,即是公平锁也是非公平锁,在new ReentrantLock()的时候可以指定类型,默认无参为非公平锁,ReentrantLock是基于AQS来实现的

其中公平锁中的tryAcquire中多了一句

hasQueuedPredecessors这个判断;也就是说每次获取锁都需要判断队列中是否还有排在前面的线程,若不存在才能继续获取锁!!,若队列还存在等待线程则获取失败,当前线程进入队列尾部

 

  

非公平锁

非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。

 

对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。


对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁

 

可重入锁 / 不可重入锁

 

可重入锁

 

广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。ReentrantLock和synchronized都是可重入锁

 

 

synchronized void setA() throws Exception{
   Thread.sleep(1000);
   setB();
}
synchronized void setB() throws Exception{
   Thread.sleep(1000);
}

 

 

上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁

不可重入锁

 

不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁。看到一个经典的讲解,使用自旋锁来模拟一个不可重入锁,代码如下

 

 

import java.util.concurrent.atomic.AtomicReference;

public class UnreentrantLock {

   private AtomicReference owner = new AtomicReference();

   public void lock() {
       Thread current = Thread.currentThread();
       //这句是很经典的“自旋”语法,AtomicInteger中也有
       for (;;) {
           if (!owner.compareAndSet(null, current)) {
               return;
           }
       }
   }

   public void unlock() {
       Thread current = Thread.currentThread();
       owner.compareAndSet(current, null);
   }
}

 

 

代码也比较简单,使用原子引用来存放线程,同一线程两次调用lock()方法,如果不执行unlock()释放锁的话,第二次调用自旋的时候就会产生死锁,这个锁就不是可重入的,而实际上同一个线程不必每次都去释放锁再来获取锁,这样的调度切换是很耗资源的。

 

把它变成一个可重入锁:

import java.util.concurrent.atomic.AtomicReference;

public class UnreentrantLock {

   private AtomicReference owner = new AtomicReference();
   private int state = 0;

   public void lock() {
       Thread current = Thread.currentThread();
       if (current == owner.get()) {
           state++;
           return;
       }
       //这句是很经典的“自旋”式语法,AtomicInteger中也有
       for (;;) {
           if (!owner.compareAndSet(null, current)) {
               return;
           }
       }
   }

   public void unlock() {
       Thread current = Thread.currentThread();
       if (current == owner.get()) {
           if (state != 0) {
               state--;
           } else {
               owner.compareAndSet(current, null);
           }
       }
   }
}

 

 

在执行每次操作之前,判断当前锁持有者是否是当前对象,采用state计数,不用每次去释放锁。

 

ReentrantLock中可重入锁实现

 

这里看非公平锁的锁获取方法:

 

 

final boolean nonfairTryAcquire(int acquires) {
   final Thread current = Thread.currentThread();
   int c = getState();
   if (c == 0) {
       if (compareAndSetState(0, acquires)) {
           setExclusiveOwnerThread(current);
           return true;
       }
   }
   //就是这里
   else if (current == getExclusiveOwnerThread()) {
       int nextc = c + acquires;
       if (nextc < 0) // overflow
           throw new Error("Maximum lock count exceeded");
       setState(nextc);
       return true;
   }
   return false;
}

 

 

在AQS中维护了一个private volatile int state来计数重入次数,避免了频繁的持有释放操作,这样既提升了效率,又避免了死锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值