Java多线程(四)Lock锁

本文介绍了Java并发编程的基础知识,包括concurrent包的三个核心部分:java.util.concurrent、java.util.concurrent.atomic和java.util.concurrent.locks。文章详细解释了synchronized关键字的局限性以及Lock接口如何提供更灵活的线程同步机制。

Java concurrent包简介

通常所说得concurrent包基本有3个package组成

  • java.util.concurrent:提供大部分关于并发的接口和类,如BlockingQueue,Callable,ConcurrentHashMap,ExecutorService等
  • java.util.concurrent.atomic:提供所有原子操作的类,如AtomicInteger,AtomicLong等
  • java.util.concurrent.locks:提供锁相关的类,如Lock,ReadWriteLock,Condition等

synchronized的缺点

如果代码被synchronized修饰了,当一个线程获取了对应的锁,并指定该代码块时,其他线程便只能一直等待,等待含有锁的线程释放锁,释放锁会有两种情况:

  1. 获取锁的线程执行完了该代码块,然后线程释放对锁的占有
  2. 线程执行发生异常,此时JVM会让线程自动释放锁

那么如果这个拥有锁的线程由于等待IO或者其他原因被阻塞了,但是又没有释放锁,这样会影响程序执行的效率。因此就需要一种机制可以不让等待的线程一直等待下去,通过Lock就可以办到。

java.util.concurrent.locks.Lock

该接口用作线程同步机制,类似于同步块。新的锁定机制更灵活,提供比同步块更多的选项。锁和同步块之间的主要区别:

  • 序列的保证 同步快不提供对等待线程进行访问的序列的任何保证,但Lock接口处理它。
  • 无超时 如果未授予锁,则同步块没有超时选项。Lock接口提供了这样的选项
  • 单一方法 同步块必须完全包含在单个方法中,而Lock接口的lock()方法和unlock()方法可以以不同的方式调用
  • Lock不是Java语言内置的,synchronized是Java语言的关键字。
  • 采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用。而Lock则必须要用户去手动释放锁,如果没有主动释放锁,则有可能会出现死锁的现象。

常用方法

public void lock()        获得锁
public void lockInterruptibly()    获取锁定,除非当前线程中断
public Condition newCondition()     返回绑定到此Lock实例的新Condition实例
public boolean tryLock()    只有在调用时才可以获得锁
public boolean tryLock(long time,TimeUnit unit)    如果在给定的等待时间内自由,并且当前线程未被中断,则获取该锁
public void unlock()     释放锁

实现类

ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法。

代码示例
public class TestLock {
    //private Lock lock = new ReentrantLock();
    public static void main(String[] args) {
        final TestLock testlock = new TestLock();
        new Thread("A") {
            public void run() {
                testlock.insert(Thread.currentThread());
            };
        }.start();
        new Thread("B") {
            public void run() {
                testlock.insert(Thread.currentThread());
            };
        }.start();
    }
    public void insert(Thread thread) {
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
            System.out.println("线程"+thread.getName()+"得到了锁。。。");
        } catch (Exception e) {
                e.printStackTrace();
        } finally {
            System.out.println("线程"+thread.getName()+"释放了锁。。。");
            lock.unlock();
        }
    }
}

运行结果

线程A得到了锁。。。
线程B得到了锁。。。
线程A释放了锁。。。
线程B释放了锁。。。

第二个线程在第一个线程释放锁之前就得到了锁。原因在于insert方法中的lock变量是局部变量,每个线程执行该方法时都会保存一个副本,那么每个线程执行到lock.lock()处获取的是不同的锁,所以就不会对临界资源形成同步互斥访问。因此,我们只需要将lock声明为成员变量即可。

public class TestLock{
    private Lock lock = new ReentrantLock();
}

运行结果:

线程A得到了锁。。。
线程A释放了锁。。。
线程B得到了锁。。。
线程B释放了锁。。。
读写锁

读写锁将对临界资源的访问分为了两个锁,一个读锁和一个写锁。正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。可以通过readLock()获取读锁,通过writeLock()获取写锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值