java中的锁

引言

上篇讲到高并发的三大特性,包括可见性,有序性和原子性,也是高并发编程需要面对的三个问题。最后讲到用锁保障操作的原子性,这篇就来介绍下锁的相关知识。

JVM中的两种锁

重量级锁synchronized,需要经过操作系统的调度,悲观锁

轻量级锁,CAS实现,不需要要经过操作系统的调度,亦称为乐观锁、无锁、自旋锁。

CAS

概念

CAS全称compare and swap,CAS操作会先取得对象当前的值,要对其进行修改时,首先判断该对象是否被修改过,没有修改则更新,修改过取当前值重复上述操作。

CAS操作的原子性

CAS是不加锁的,可以多个线程对一个对象进行CAS操作,那么怎么保证原子性呢,实际CAS的底层实现是lock cmpxchg,也是加锁的。

java中CAS的常见实现类
AtomicInteger:对应Integer类型,线程安全
public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
Unsafe:底层是通过地址偏移,对JVM中内存进行直接操作
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

synchronized

发展

早期只有重量级锁,每次使用需要jvm向内核申请 ,发起80中断。

现在使用会随着线程的竞争,将锁一步步进行升级,升级过程为:无锁状态->偏向锁->自旋锁->重量级锁。升级过程中锁的信息会放在锁定对象的markdown里面。

锁升级过程
  1. 开始对象处于无锁状态

  2. 有线程获得锁,升级为偏向锁

  3. 有线程竞争,升级为自旋锁

  4. 竞争的线程达到一定数量,升级为重量级锁

其他特性
  1. 可重入性
  2. wait释放锁,sleep不释放锁,join只会释放Thread的锁
  3. synichronized锁的是对象,不是代码
  4. 保障可见性
  5. 临界执行时间长,等的线程多时适合使用

JUC中的各种锁

ReentrantLock

使用场景

代替synchronized加锁,保证原子性

常规使用
Lock lock = new ReentrantLock();
void m1() {
		try {
			lock.lock(); //与synchronized类似,可替代其
			for (int i = 0; i < 10; i++) {
				TimeUnit.SECONDS.sleep(1);

				System.out.println(i);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
设置为公平锁
ReentrantLock lock=new ReentrantLock(true);
尝试锁定
lock.tryLock(5, TimeUnit.SECONDS)
设置为可打断
lock.lockInterruptibly(); //可以对interrupt()方法做出响应

CountDownLatch

使用场景

阻塞一个线程,由其他线程调用countDown的次数决定是否打开阻塞。

常规使用
private static void usingCountDownLatch() {
        Thread[] threads = new Thread[10];
        CountDownLatch latch = new CountDownLatch(threads.length);//设置门闩数

        for(int i=0; i<threads.length; i++) {
            threads[i] = new Thread(()->{
             // 将门闩减一
             latch.countDown();
            });
        }

        for (int i = 0; i < threads.length; i++) {
            threads[i].start();
        }

        try {
            latch.await();// 阻塞住
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("end latch");
    }

Semaphore

使用场景

指定线程数限流

常规使用
        //允许一个线程同时执行
        Semaphore s = new Semaphore(1);

        new Thread(()->{
            try {
                // 获取信号量,可以往下执行
                s.acquire();
                System.out.println("T1 running...");
                // 释放一个信号量,其他线程可以再获得
//                s.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } 
        }).start();

		new Thread(()->{
            try {
                s.acquire();

                System.out.println("T2 running...");
                
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

CyclicBarrier

使用场景

阻塞一定数目线程,线程数满了后,执行指定方法。阻塞的线程也能执行下去。

常规使用
public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(20, () -> System.out.println("满人"));

        for(int i=0; i<100; i++) {

                new Thread(()->{
                    try {
                        barrier.await();
                        System.out.println("我执行了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }).start();
            
        }
    }

Phaser

使用场景

设置一个个阶段,线程达到此阶段阻塞住,每个阶段达到一定的线程后,执行该阶段的任务,并释放阻塞。

常规使用
 public static void main(String[] args) {
        MarriagePhaser phaser = new MarriagePhaser();
     // 设置每个阶段需要线程到达数
        phaser.bulkRegister(5);

        for(int i=0; i<5; i++) {
            final int nameIndex = i;
            new Thread(()->{

                Person p = new Person("person " + nameIndex);
                p.arrive();
                // 阻塞,达到数目后打开,phaser的阶段提升一级
                phaser.arriveAndAwaitAdvance();

                p.eat();
                phaser.arriveAndAwaitAdvance();

                p.leave();
                phaser.arriveAndAwaitAdvance();
            }).start();
        }

    }


    static class MarriagePhaser extends Phaser {
        
        // 满足线程数时执行的方法
        @Override
        protected boolean onAdvance(int phase, int registeredParties) {

            switch (phase) {
                case 0:
                    System.out.println("所有人到齐了!");
                    return false;
                case 1:
                    System.out.println("所有人吃完了!");
                    return false;
                case 2:
                    System.out.println("所有人离开了!");
                    System.out.println("婚礼结束!");
                    return true;
                default:
                    return true;
            }
        }
    }

LockSupport

使用场景

可用来替代wait和 notify

常规使用
LockSupport.park();//阻塞
LockSupport.unpark(t); // 打开线程的阻塞
ReadWriteLock
使用场景

读多写少,读不加锁,写加锁

常规使用
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static Lock readLock = readWriteLock.readLock();  //读锁
    static Lock writeLock = readWriteLock.writeLock();//写锁

Exchanger

使用场景

两个线程一起交换数据后,一起往下走

常规使用
static Exchanger<String> exchanger = new Exchanger<>();

    public static void main(String[] args) {
        new Thread(()->{
            String s = "T1";
            try {
                s = exchanger.exchange(s);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " " + s);

        }, "t1").start();


        new Thread(()->{
            String s = "T2";
            try {
                s = exchanger.exchange(s);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " " + s);

        }, "t2").start();


    }

AQS

上述JUC的锁都是AQS实现的

AQS原理

CAS + AQS + volatile

  1. 变量state由voaltile修饰,根据子类的实现取不同的值 (重入次数)
  2. 双向链表,实现任务队列,放来操作state的队列。
  3. CAS判断能否拿锁,拿不到锁队尾入队列。
  4. 头部节点操作state,由于是双端队列,头部第二个节点可以判断头节点是否执行结束,结束了移出其,并获得锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

平平安安年年

一起学习,一起成长

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值