获取中...

-

Just a minute...

引入Lock的原因

Java提供了一个关键字synchronized用来保证同步访问,那为什么还需要使用Lock?最根本的原因就是使用synchronized会造成线程的阻塞,资源同时只能被一个线程访问,程序效率低。比如,多个线程查询读写文件的时候必须使用同步访问,但是如果使用synchronized的话,不管是读操作还是写操作,都只能有一个线程访问,实际上如果所有的线程都是读取资源文件,不去做同步操作也是可以的,只有当对文件做写操作的时候才需要对资源加锁,防止文件在写入的时候有其他的线程读取文件内容。Lock的引入就可以解决这种问题。

还有一点就是synchronized没有办法显式的释放锁,synchronized只能在代码运行结束或者出现异常的时候才可以释放锁,而Lock可以显示的释放锁,这一点,让同步的机制更加灵活,开发也更加灵活。

Lock中常用的方法

Lock在是Java中的一个接口,里面声明了一些方法:

  • lock():用来获取锁,如果没有获取到,则等待。
  • unLock():释放锁,需要注意的是Lock是需要显式的释放锁,所以需要将lock()放在try块中,unLock()方法放在finally块中,防止异常造成死锁现象。
  • tryLock():尝试获取锁,如果获取到锁,则返回true,否则返回false,这个方法不是阻塞的,也就是说会立刻返回结果。
  • tryLock(long time, TimeUnit unit):这个方法和tryLock()类似,不一样的是这个方法如果没有获取到锁,会等待指定的时间,如果指定的时间没有获取到锁,返回false,获取到锁则返回true。
  • lockInterruptibly():当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。

ReentrantLock

ReentrantLock是Lock的唯一实现,这是一个重入锁。主要实现了Lock中的方法,并且提供一些新的方法,具体的使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class ReentrantLockThread implements Runnable {

private Integer value = 0;
private Lock lock = new ReentrantLock();

@Override
public void run() {

try {
lock.lock();
value++;
System.out.println(Thread.currentThread().getName() + "获取了锁: value is " + value);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + "释放了锁: value is " + value);

}

}

public static void main(String[] args) {

ReentrantLockThread reentrantLockThread = new ReentrantLockThread();

Thread thread1 = new Thread(reentrantLockThread, "thread1");
Thread thread2 = new Thread(reentrantLockThread, "thread2");
Thread thread3 = new Thread(reentrantLockThread, "thread3");

thread1.start();
thread2.start();
thread3.start();
}

}

运行结果为:

1
2
3
4
5
6
thread1获取了锁: value is 1
thread1释放了锁: value is 1
thread2获取了锁: value is 2
thread2释放了锁: value is 2
thread3获取了锁: value is 3
thread3释放了锁: value is 3

ReadWriteLock

ReadWriteLock是Java中定义的一个接口,主要是用来表示读写锁的。ReadWriteLock中主要有两个方法:

  • readLock():返回一个读锁
  • writeLock():返回一个写锁

ReentrantReadWriteLock

ReentrantReadWriteLock是ReadWriteLock的实现,里面实现了获取读写锁的逻辑和其他的一些方法。具体的使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class ReadWriteLockDemo {

private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

public void print() {
for (int i = 0; i < 10; i++) {
try {
readWriteLock.readLock().lock();
System.out.println(
Thread.currentThread().getName() + "读取" + i + ",时间为:" + System.currentTimeMillis());
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}

}
}

public static void main(String[] args) {

ReadWriteLockDemo readWriteLockDemo = new ReadWriteLockDemo();

new Thread(() -> {
readWriteLockDemo.print();
}).start();
new Thread(() -> {
readWriteLockDemo.print();
}).start();
new Thread(() -> {
readWriteLockDemo.print();
}).start();

}
}

运行结果如下,可以看到在同一个时间点,不同的线程也在打印:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Thread-0读取0,时间为:1516200749523
Thread-0读取1,时间为:1516200749524
Thread-0读取2,时间为:1516200749524
Thread-0读取3,时间为:1516200749524
Thread-0读取4,时间为:1516200749525
Thread-0读取5,时间为:1516200749525
Thread-0读取6,时间为:1516200749525
Thread-0读取7,时间为:1516200749525
Thread-1读取0,时间为:1516200749525
Thread-1读取1,时间为:1516200749526
Thread-1读取2,时间为:1516200749526
Thread-0读取8,时间为:1516200749526
Thread-1读取3,时间为:1516200749526
Thread-1读取4,时间为:1516200749526
Thread-1读取5,时间为:1516200749526
Thread-0读取9,时间为:1516200749526
Thread-1读取6,时间为:1516200749526
Thread-1读取7,时间为:1516200749526
Thread-1读取8,时间为:1516200749526
Thread-2读取0,时间为:1516200749526
Thread-2读取1,时间为:1516200749527
Thread-1读取9,时间为:1516200749526
Thread-2读取2,时间为:1516200749527
Thread-2读取3,时间为:1516200749527
Thread-2读取4,时间为:1516200749527
Thread-2读取5,时间为:1516200749527
Thread-2读取6,时间为:1516200749527
Thread-2读取7,时间为:1516200749527
Thread-2读取8,时间为:1516200749527
Thread-2读取9,时间为:1516200749527

关于读写锁,如果一个线程已经获取了读锁,其他的线程还可以获取到读锁,不能获取到写锁。如果一个线程获取到了写锁,其他的线程读锁和写锁都不能获取到,只能等待锁释放。

Lock和synchronized的异同

相同点

  • Lock和synchronized都是重入锁,都可以实现同步

区别

  • Lock是一个接口,synchronized是一个关键字,是Java的内置实现。
  • Lock是一个可中断的锁,可以让等待的线程中断等待,而synchronized不可以中断,只能等待代码执行完成或者出现异常
  • Lock必须手动获取锁,同时也必须手动的释放锁,使用的时候需要考虑死锁的问题,而synchronized不需要手动释放,不会存在死锁的问题
  • Lock是可以判断是否获取到锁的,使用更加灵活
  • Lock在读写操作的场景下效率比synchronized高
  • Lock可以实现公平锁,而synchronized不是公平锁。公平锁是以请求锁的顺序来获取锁。比如同时有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程会获得该锁。Lock通过在创建对象的时候指定是否为公平锁。
相关文章
评论
分享
  • Java多线程之同步容器

    同步容器的作用Java中的容器主要为List、set、Map、Queue,这些容器都有不同的实现类,比如ArrayLIst、HashSet、HashMap、PriorityQueue等等,但是大部分都是非线程安全的,意味着在多线程访问...

    Java多线程之同步容器
  • Java多线程之ThreadLocal

    ThreadLocal的作用ThreadLocal的作用是提供线程内的局部变量,这个变量在同一个线程的生命周期内起作用,可以减少同一个线程内多个方法之前传入公共资源的复杂性,并且隔离其他线程。下面是一个使用ThreadLocal的例子...

    Java多线程之ThreadLocal
  • Java多线程之synchronized

    多线程引发的安全问题由于线程是共享进程中的所有资源的,那么每个线程都可以去操作进程中的某一个资源,当多个线程操作同一个资源的时候就可能出现线程安全问题。比如两个线程对同一个实例变量做加1的操作,然后打印该变量,如果变量的初始值为0,当...

    Java多线程之synchronized
  • Java中的线程的创建

    Java创建线程的三种方式 继承Thread 实现Runnable 实现Callable 继承Thread创建线程通过继承Thread的方式,可以很快的创建一个线程,Thread中有一个run()方法,只需要重写该方法,将需要另外开...

    Java中的线程的创建
  • 线程和进程

    什么是进程在百度百科中,进程有两种定义,一种是狭义定义:进程是正在运行的程序实例,比如微信、Tomcat、MySQL,这些就是一个个进程。另一种就是广义的定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,它是操作...

    线程和进程
  • Redis过期策略和内存淘汰策略

    Redis处理过期的keyRedis会将每个设置了过期时间的key保存在一个独立的字典中,以后会定期扫描这个字典来删除过期了的key。除了定期扫描删除策略之外,Redis同时采用惰性删除策略,在客户端访问key的时候,Redis会对这...

    Redis过期策略和内存淘汰策略
  • Redis持久化:AOF

    什么是AOF持久化Redis有两种持久化方式,RDB和AOF。RDB是将Redis中的数据保存一份到RDB文件中,而AOF不是保存键值对数据,而是保存服务器执行的写命令来记录数据库的状态。 AOF持久化的实现原理AOF持久化的实现可...

    Redis持久化:AOF
  • Redis持久化:RDB

    什么是RDB持久化RDB持久化是Redis的持久化方式之一,也被称为快照持久化,是将某一个时间节点的Redis内存中数据保存到磁盘上,生成一个RDB文件。之后,用户可以将这份文件进行备份,复制到其他的服务器上创建具有相同状态的Redi...

    Redis持久化:RDB
  • jps命令详解

    jps命令的作用jps命令是JDK中自带的命令,位于bin目录下,其作用是显示当前用户下系统中所有的Java进程的相关信息,比如pid。有了这个命令,可以很方便的显示Java进程,不过显示的是当前用户的所有Java进程,不能根据项目名...

    jps命令详解
Please check the parameter of comment in config.yml of hexo-theme-Annie!