Java中的锁详解

1.重入锁Reentrantlock

简单举例:

import java.util.concurrent.locks.ReentrantLock;

public class ReenterLock implements Runnable {
    public static ReentrantLock lock = new ReentrantLock();
    public static int i = 0;

    @Override
    public void run() {
        for (int j = 0; j < 1000; j++) {
            lock.lock();
            try {
                i++;
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReenterLock tl = new ReenterLock();
        Thread t1 = new Thread(tl);
        Thread t2 = new Thread(tl);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

重入锁是指当前线程可以反复进入同一个锁。

如:

    @Override
    public void run() {
        for (int j = 0; j < 1000; j++) {
            lock.lock();
            lock.lock();
            try {
                i++;
            } finally {
                lock.unlock();
                lock.unlock();
            }
        }
    }

与synchronized相比,重入锁可进行中断响应

如下:

import java.util.concurrent.locks.ReentrantLock;

public class IntLock implements Runnable {
    public static ReentrantLock lock1 = new ReentrantLock();
    public static ReentrantLock lock2 = new ReentrantLock();
    int lock;

    public IntLock(int lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            if (lock == 1) {
                lock1.lockInterruptibly();//可以对中断进行响应的锁请求
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {

                }
                lock2.lockInterruptibly();
                System.out.println("线程完成!");
            } else {
                lock2.lockInterruptibly();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {

                }
                lock1.lockInterruptibly();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock1.isHeldByCurrentThread())
                lock1.unlock();
            if (lock2.isHeldByCurrentThread())
                lock2.unlock();
            System.out.println(Thread.currentThread().getId() + ":线程退出");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        IntLock intLock1 = new IntLock(1);
        IntLock intLock2 = new IntLock(2);
        Thread t1 = new Thread(intLock1);
        Thread t2 = new Thread(intLock2);
        t1.start();
        t2.start();
        Thread.sleep(1000);
        t2.interrupt();
    }
}

在代码最后,由于线程t2中断,所以t2会放弃对lock1的申请,同时放弃已经获得的lock2。该操作会导致t1可以顺利得到lock2并执行下去。因此,最后的结果会打印一个线程完成,两个线程退出。

并且可以进行锁申请等待限时

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class TimeLock implements Runnable {
    public static ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        try {
            if (lock.tryLock(5, TimeUnit.SECONDS)) {
                Thread.sleep(6000);
                System.out.println(Thread.currentThread().getId()+":Job Done!");
            } else {
                System.out.println(Thread.currentThread().getId()+":get lock failed");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        TimeLock timeLock = new TimeLock();
        Thread t1 = new Thread(timeLock);
        Thread t2 = new Thread(timeLock);
        t1.start();
        t2.start();
    }
}

由于t1等待6s,导致t2在5s之内并没有获得锁,所以t2输出get lock failed,而t1输出job done.

重入锁还可以进行公平性设定,意思是当线程1和线程2都在申请锁,非公平锁是随机选择线程获得锁,而公平锁是先到先得。但是由于公平锁需要维护一个有序队列,因此其性能较低,在默认情况下是非公平的。

总结:synchronized关键字与可重入锁的区别

1.可重入性
二者均可重入
2.锁的实现
synchronized依赖于JVM实现,而ReentrantLock基于JDK实现,通过阅读源码可以了解实现,区别就类似于操作系统控制实现与用户使用代码实现。
3.性能区别
在synchronized优化以前,性能比ReentrantLock差很多,但自从synchronize引入了偏向锁、轻量级锁(自选锁)后 ,也就是自循锁后,两者性能差不多(JDK1.6以后,为了减少获得锁和释放锁所带来的性能消耗,提高性能,引入了“轻量级锁”和“偏向锁”)。
4.功能区别

  1. 便利性:synchronized更便利,它是由编译器保证加锁与释放。ReentrantLock是需要手动声明与释放锁,所以为了避免忘记手工释放锁造成死锁,所以最好在finally中声明释放锁。
  2. 锁的细粒度和灵活度:ReentrantLock优于synchronized

5.ReentrantLock独有的功能:

  1. ReentrantLock可以指定是公平锁还是非公平锁,synchronized只能是非公平锁。(所谓公平锁就是先等待的线程先获得锁)
  2. 提供了一个Condition类,可以分组唤醒需要唤醒的线程。不像是synchronized要么随机唤醒一个线程,要么全部唤醒。
  3. 提供能够中断等待锁的线程的机制

2.sychronized的锁优化

锁偏向

核心思想:如果一个线程获得了锁,那么锁就进入偏向模式。当这个线程再次请求锁时,无须再做任何同步操作。

轻量级锁

核心思想:将对象头部作为指针,指向持有锁的线程堆栈的内部,来判断一个线程是否持有对象锁。如果线程获得轻量级锁成果,则可以进入到临界区,否则则会膨胀为重量级锁。

自旋锁

核心思想:锁膨胀后,为避免线程挂起,虚拟机会让当前线程做几个空循环,在经过若干次循环后,若能获得锁,则进入临界区,否则挂起。

锁消除

核心思想:Java虚拟机再JIT编译时,通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁。

3.ConcurrentHashmap分段锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值