Java高并发--重入锁

重入锁(Java.util.concurrent.locks.ReentrantLock):用来替代synchronized、notify()、notifyAll()。

ReentrantLock得几个重要方法整理:

  • lock():获得锁,如果锁被占用,则等待。
  • lockInterruptibly():获得锁,但优先响应中断;
  • tryLock():尝试获得锁,如果成功,返回true;如果返回false,不等待,直接返回;
  • unlock():释放锁。
//如果锁没有被其他线程占有,该线程立即得到该锁
//如果该线程已经占有了该锁,那锁的计数+1
public void lock() {   sync.lock();}

//如果该锁被当前线程占有,那么计数器-1,如果计数器归零,锁释放。
//如果锁不归当前线程所有,那么抛出异常:IllegalMonitorStateException
public void unlock() {  sync.release(1);}

中断响应

对于synchronized来说,如果一个线程正在等待锁,那么只要两种情况:

  • 获得该锁继续执行;
  • 保持等待;

当使用了重入锁,就另外多了一种可能性:

  • 线程可以被中断。

即在等待锁的过程中,程序可以根据需要取消对锁的请求。

/**
 * 获取锁,除非线程被打断;
 * 如果锁不被其他线程持有,则获取锁,并立即返回,计数器=1;
 * 如果当前线程已经持有此锁,那么计数器+1;
 * 如果锁被另外一个线程持有,那么当前线程将处于线程调度的目的被禁用,
 * 并处于休眠状态,直到发生以下两个情况:
 * 1.锁由当前线程获取;
 * 2.其他线程打断当前线程。
 * 
 * 如果当前线程:进入此方法,如果中断状态已经设置,或者
 * 获取锁被打断,
 * 则抛出interruptedException
 */
public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

公平锁

大多数情况下,锁的申请都是非公平的。

公平锁:按照时间的先后顺序,保证先到者先得,不会产生饥饿现象

synchronized关键字进行锁控制,锁就是非公平得。而重入锁可以对公平性进行设置

//当fair是true时,锁是公平得
//默认情况下锁是非公平得,因为公平锁需要维护一个有序队列,性能相对低
public ReentrantLock(boolean fair)
public class FairLock implements Runnable {
    //true表示此对象得锁是公平得
    public static ReentrantLock fairLock = new ReentrantLock(true);
    public void run() {
        while (true){
            try {
                fairLock.lock();
                System.out.println(Thread.currentThread().getName() + "--->获得锁");
            }finally {
                fairLock.unlock();
            }
        }
    }
    public static void main(String[] args) {
        FairLock fl = new FairLock();
        Thread t1 = new Thread(fl, "Thread_t1");
        Thread t2 = new Thread(fl, "Thread_t2");
        t1.start();
        t2.start();
    }
}

上述代码由于是公平锁,所以应该是t1和t2轮流得到锁。

Condition条件

condition和wait()和notify()方法作用大致相同,但是配合重入锁使用

通过Lock接口的Condition newCondition()方法可以生成一个与重入锁绑定的实例。

//使当前线程等待,同时释放当前锁,当其他线程使用signal()或者signalAll()时,会重新获得该锁并继续执行
void await() throws InterruptedException;

//与await()方法基本相同,但是不会在等待过程中响应中断
void awaitUninterruptibly();

//signal()方法唤醒一个在等待中得线程。
void signal();
void signalAll();
public class ReenterLockCondition implements Runnable{
    public static ReentrantLock lock = new ReentrantLock();
    public static Condition condition = lock.newCondition();
    public void run() {
        try {
          	//1.对象得到锁
            lock.lock();
            //2.要求线程在Condition对象上等待
            condition.await();
            System.out.println("Thread is going on");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReenterLockCondition t = new ReenterLockCondition();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        Thread.sleep(2000);
        //3.通知t1继续执行
        lock.lock();
        //4.唤醒线程
        condition.signal();
        lock.unlock();
    }
}

信号量(Semaphore):允许多个线程同时访问

无论是synchronized还是重入锁,一次只允许一个线程访问一个资源。

信号量却可以指定多个线程,同时访问某一个资源。

public Semaphore(int permits)
//第二个参数可以指定是否公平
public Semaphore(int permits, boolean fair)
//尝试获取一个准入得许可,无法获得则等待
public void acquire();
public void acquireUninterruptibly();

//尝试获得一个许可,成功返回true,失败返回false。
public boolean tryAcquire();
public boolean tryAcquire(long timeout, TimeUnit unit);

//线程访问资源结束后,释放许可
public void release();

下面是一个实例:

public class SemapDemo implements Runnable{
    final Semaphore semp = new Semaphore(5);
    public void run() {
        try {
            semp.acquire();
            //为临时区管理代码,意味着同时可以由5个线程进入这段代码
            //模拟耗时操作
            Thread.sleep(2000);
            System.out.println(Thread.currentThread().getId()+":done!");
            semp.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        //同时开启20个线程
        ExecutorService exec = Executors.newFixedThreadPool(20);
        final SemapDemo semapDemo = new SemapDemo();
        for (int i = 0; i < 20; i++) {
            //最终会发现线程是5个为一组进入临时区
            exec.submit(semapDemo);
        }
    }
}

读写锁:ReadWriteLock

读写分离锁可以有效帮助减少锁竞争;

读写锁允许多个线程同时读,真正并行,写写操作和读写操作之间还是要相互等待和持有锁。

倒计时锁:CountDownLatch

倒计时锁是个非常实用得多线程控制工具类。

通常用来控制线程等待,可以让某个线程等待直到倒计时结束,再开始执行

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值