设为首页 加入收藏

TOP

Java高并发之锁的使用以及原理浅析(一)
2019-08-13 05:36:25 】 浏览:119
Tags:Java 并发 使用 以及 原理 浅析

本文将从下面几个方面介绍


    在学习或者使用Java的过程中进程会遇到各种各样的锁的概念:公平锁、非公平锁、自旋锁、可重入锁、偏向锁、轻量级锁、重量级锁、读写锁、互斥锁等待。下边总结了对各种锁的解释


    公平锁是指多个线程在等待同一个锁时按照申请锁的先后顺序来获取锁。相反的非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。


     公平锁的好处是等待锁的线程不会饿死,但是整体效率相对低一些;非公平锁的好处是整体效率相对高一些,但是有些线程可能会饿死或者说很早就在等待锁,但要等很久才会获得锁。其中的原因是公平锁是严格按照请求所的顺序来排队获得锁的,而非公平锁时可以抢占的,即如果在某个时刻有线程需要获取锁,而这个时候刚好锁可用,那么这个线程会直接抢占,而这时阻塞在等待队列的线程则不会被唤醒。


        对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。例:new ReentrantLock(true)是公平锁
对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。


    也叫递归锁,是指在外层函数获得锁之后,内层递归函数仍然可以获取到该锁。即线程可以进入任何一个它已经拥有锁的代码块。在JAVA环境下 ReentrantLock 和synchronized 都是可重入锁。可重入锁最大的作用是避免死锁。


   具体区别下文阐述。


    在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。


      JDK6中已经变为默认开启自旋锁,并且引入了自适应的自旋锁。自适应意味着自旋的时间不在固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。自旋是在轻量级锁中使用的,在重量级锁中,线程不使用自旋。


      这三种锁是指锁的状态,并且是针对Synchronized在Java 5后通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。如下图


       


                                这里的无锁和偏向锁在对象头的倒数第三bit中分别采用0和1标记


      乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度


  独占锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。


   ReentrantLock,可重入锁,是一种递归无阻塞的同步机制。它可以等同于synchronized的使用,但是ReentrantLock提供了比synchronized更强大、灵活的锁机制,可以减少死锁发生的概率。


   ReentrantLock还提供了公平锁和非公平锁的选择,构造方法接受一个可选的公平参数(默认非公平锁),当设置为true时,表示公平锁,否则为非公平锁。


    一般使用如下方式获取锁


 lock方法:


      Sync为Sync为ReentrantLock里面的一个内部类,它继承AQS。关于AQS的相关知识可以自行补充一下。Sync有两个子类分别是FairSync(公平锁)和 NofairSync(非公平锁)。默认使用NofairSync,下面是ReentrantLock的构造类


    下边是一个简单的重入锁使用案例


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


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


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


       上述代码的第8~12行,使用了重入锁保护了临界区资源i,确保了多线程对i的操作。输出结果为2000000。可以看到与synchronized相比,重入锁必选手动指定在什么地方加锁,什么地方释放锁,所以更加灵活。


要注意是,再退出临界区的时候,需要释放锁,否则其他线程就无法访问临界区了。这里为啥叫可重入锁是因为这种锁是可以被同一个线程反复进入的。比如上述代码??使用锁部分可以写成这样


        在这种情况下,一个线程联连续两次获取同一把锁,这是允许的。但是需要注意的是,

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Java并发之阻塞队列浅析 下一篇Java中通过Hibernate-Validation..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目