Java高并发编程学习--11. 自定义的BooleanLock

一、用boolean自定义锁

BooleanLock的功能:

  • (1) 多个线程通过lock()方法争抢锁
  • (2) 可中断被阻塞的线程
  • (3) 阻塞的线程可超时

Lock.java接口

package p96_boolean_lock;

import java.util.List;
import java.util.concurrent.TimeoutException;

/**
 * Lock接口
 */
public interface Lock {
    /**
     * lock()方法永远阻塞,除非获得了锁,和synchronized类似,但该方法可以被中断,中断时
     * 抛出InterruptedException异常
     * @throws InterruptedException
     */
    void lock() throws InterruptedException;

    /**
     * lock(long mills)除了可以被中断外,还增加了超时功能
     * @param mills
     * @throws InterruptedException
     * @throws TimeoutException
     */
    void lock(long mills) throws InterruptedException, TimeoutException;

    /**
     * unlock()方法用来进行锁的释放
     */
    void unlock();

    /**
     * 用来获取有哪些线程被阻塞
     * @return
     */
    List<Thread> getBlockedThreads();
}

BooleanLock.java实现Lock接口

package p96_boolean_lock;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeoutException;

import static java.lang.System.currentTimeMillis;
import static java.lang.Thread.currentThread;

/**
 * @ClassName BooleanLock
 * @Description TODO
 * @Author Cays
 * @Date 2019/5/3 16:20
 * @Version 1.0
 **/
public class BooleanLock implements Lock {
    //代表当前拥有锁的线程
    private Thread currentThread;
    //locked是一个Boolean开关,false代表当前该锁没有被任何线程获得
    //或者已经释放,true代表该锁已经被某个线程获得,即currentThead
    private boolean locked = false;
    /**
     * blocked List存放哪些线程在获取当前线程时进入了阻塞状态
     */
    private final List<Thread> blockedList = new ArrayList<>();

    @Override
    public void lock() throws InterruptedException {
        // 1. lock()方法使用同步代码块的方式进行同步
        synchronized (this){
            // 2. 如果当前锁已经被某个线程获得,则该线程将加入阻塞队列,并使当前线程
            // wait释放对this monitor的所有权
            while (locked){
                /**
                 * 如果某个线程被中断,那么它将有可能还存在于blockList中,修复如下:
                 */
                //暂存当前线程
                final Thread tempThread = currentThread();
                try{
                    if (!blockedList.contains(tempThread)){
                        blockedList.add(tempThread);
                    }
                    this.wait();
                }catch (InterruptedException e){
                    //如果当前线程在wait时被中断,则从blockList中将其移除,避免内存泄漏
                    blockedList.remove(tempThread);
                    //继续抛出异常
                    throw e;
                }
            }
            /**
             * 3. 如果当前锁没有被其他线程获得,该线程尝试从阻塞队列删除自己,如果当前线程
             * 从未进入过阻塞队列,删除方法不会有任何影响;如果当前线程是从wait set中被唤
             * 醒的,则需要从阻塞队列中删除自己
             */
            blockedList.remove(currentThread());
            // 4. locked开关设为true
            this.locked=true;
            // 5. 记录获取锁的过程
            this.currentThread=currentThread();
        }
    }

    @Override
    public void lock(long mills) throws InterruptedException, TimeoutException {
        // 1. 如果mills不合法,默认调用lock()方法,也可以抛出异常
        synchronized (this) {
            if (mills <= 0) {
                this.lock();
            } else {
                System.out.println("enter locked:"+locked);
                long remainingMills = mills;
                long endMills = currentTimeMillis() + remainingMills;
                while (locked) {
                    //暂存当前线程
                    final Thread tempThread = currentThread();
                    try {
                        // 2. 如果remainingMills <= 0说明当前线程被其他线程唤醒或者在指定时间
                        // wait没有获得锁,这种情况下抛出异常
                        if (remainingMills <= 0) {
                            throw new TimeoutException("can not get lock during " + mills+" ms.");
                        }
                        if (!blockedList.contains(tempThread)) {
                            blockedList.add(tempThread);
                        }
                        /**
                         * 3. 等待remainingMills的毫秒数,最开始由其他线程传入,之后wait
                         * 会重新计算
                         */
                        this.wait(remainingMills);
                        // 4. 重新计算remainingMills的时间
                        remainingMills = endMills - currentTimeMillis();
                    } catch (InterruptedException | TimeoutException e) {
                        //如果当前线程在wait时被中断,则从blockList中将其移除,避免内存泄漏
                        blockedList.remove(tempThread);
                        //继续抛出异常
                        throw e;
                    }
                }
                /**
                 * 5. 获得该锁,并且从block的列表中删除当前线程,将locked的状态修改为true并
                 * 指定获得锁的线程就是当前线程
                 */
                blockedList.remove(currentThread());
                this.locked = true;
                System.out.println("currentThread="+currentThread());
                this.currentThread = currentThread();
            }
        }
    }

    @Override
    public void unlock() {
        synchronized (this){
            /**
             * 1. 判断当前线程是否是加锁的线程,只有加了锁的线程才有资格解锁
             */
            //System.out.println("currentThread:"+currentThread);
            //System.out.println("currentThread:"+currentThread());
            if (currentThread == currentThread()){
                // 2. 将locked的状态修改为false
                this.locked=false;
                Optional.of(currentThread().getName()+" release the lock.")
                        .ifPresent(System.out::println);
                // 3. 通知其他在wait set的线程,可以争抢锁
                this.notifyAll();
            }
        }
    }

    @Override
    public List<Thread> getBlockedThreads() {
        return Collections.unmodifiableList(blockedList);
    }

}

二、多个线程通过lock()方法争抢锁

打开(1)处注释

package p96_boolean_lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.IntStream;

import static java.lang.Thread.currentThread;
import static java.util.concurrent.ThreadLocalRandom.current;

/**
 * @ClassName BooleanLockTest
 * @Description TODO
 * (1) 多个线程通过lock()方法争抢锁
 * (2) 可中断被阻塞的线程
 * @Author Cays
 * @Date 2019/5/3 16:54
 * @Version 1.0
 **/
public class BooleanLockTest {
    // 1. 定义BooleanLock
    private final Lock lock=new BooleanLock();
    // 2. 使用try/finally语句确保lock每次都能被正确释放
    public void syncMethod(){
        try{
            //加锁
            lock.lock();
            int randomInt = 1;
            System.out.println(currentThread()+" get the lock.");
            //休眠10s
            TimeUnit.MILLISECONDS.sleep(10);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            //释放锁
            lock.unlock();
        }
    }
    public void syncMethodTimeoutable(){
        try{
            lock.lock(1000);
            System.out.println(currentThread()+" get the lock.");
            int randomInt = 10; //current().nextInt(10);
            //休眠10s
            TimeUnit.SECONDS.sleep(randomInt);
        }catch (InterruptedException | TimeoutException e){
            //System.out.println("超时");
            e.printStackTrace();
        }finally {
            //System.out.println("超时2");
            lock.unlock();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        BooleanLockTest booleanLockTest=new BooleanLockTest();
        // 3. 定义一个线程并启动
        //(1) 多个线程通过lock()方法争抢锁
        IntStream.range(0,10)
                .mapToObj(i->new Thread(booleanLockTest::syncMethod))
                .forEach(Thread::start);
        /**
         * (2) 可中断被阻塞的线程
         */
        /*new Thread(booleanLockTest::syncMethod,"T1").start();
        TimeUnit.MILLISECONDS.sleep(2);
        Thread t2=new Thread(booleanLockTest::syncMethod,"T2");
        t2.start();
        TimeUnit.MILLISECONDS.sleep(10);
        t2.interrupt();
        TimeUnit.MILLISECONDS.sleep(10);*/
        /**
         * (3) 阻塞的线程可超时
         */
        /*new Thread(booleanLockTest::syncMethodTimeoutable,"T1").start();
        TimeUnit.MILLISECONDS.sleep(10);
        Thread t2 = new Thread(booleanLockTest::syncMethodTimeoutable,"T2");
        t2.start();
        TimeUnit.MILLISECONDS.sleep(10);*/
    }
}

只有当前一个线程释放monitor的锁后,后一个线程才能继续执行。
结果:
在这里插入图片描述

三、可中断被阻塞的线程

打开(2)处注释

package p96_boolean_lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.IntStream;

import static java.lang.Thread.currentThread;
import static java.util.concurrent.ThreadLocalRandom.current;

/**
 * @ClassName BooleanLockTest
 * @Description TODO
 * (1) 多个线程通过lock()方法争抢锁
 * (2) 可中断被阻塞的线程
 * @Author Cays
 * @Date 2019/5/3 16:54
 * @Version 1.0
 **/
public class BooleanLockTest {
    // 1. 定义BooleanLock
    private final Lock lock=new BooleanLock();
    // 2. 使用try/finally语句确保lock每次都能被正确释放
    public void syncMethod(){
        try{
            //加锁
            lock.lock();
            int randomInt = 10;
            System.out.println(currentThread()+" get the lock.");
            //休眠10s
            TimeUnit.SECONDS.sleep(randomInt);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            //释放锁
            lock.unlock();
        }
    }
    public void syncMethodTimeoutable(){
        try{
            lock.lock(1000);
            System.out.println(currentThread()+" get the lock.");
            int randomInt = 10; //current().nextInt(10);
            //休眠10s
            TimeUnit.SECONDS.sleep(randomInt);
        }catch (InterruptedException | TimeoutException e){
            //System.out.println("超时");
            e.printStackTrace();
        }finally {
            //System.out.println("超时2");
            lock.unlock();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        BooleanLockTest booleanLockTest=new BooleanLockTest();
        // 3. 定义一个线程并启动
        //(1) 多个线程通过lock()方法争抢锁
        /*IntStream.range(0,10)
                .mapToObj(i->new Thread(booleanLockTest::syncMethod))
                .forEach(Thread::start);*/
        /**
         * (2) 可中断被阻塞的线程
         */
        //new Thread(booleanLockTest::syncMethod,"T1").start();
        //TimeUnit.MILLISECONDS.sleep(2);
        Thread t2=new Thread(booleanLockTest::syncMethod,"T2");
        t2.start();
        TimeUnit.MILLISECONDS.sleep(10);
        t2.interrupt();
        TimeUnit.MILLISECONDS.sleep(10);
        System.out.println("T2 state:"+t2.getState());
        System.out.println("T2 interrupt:"+t2.isInterrupted());
        /**
         * (3) 阻塞的线程可超时
         */
        /*new Thread(booleanLockTest::syncMethodTimeoutable,"T1").start();
        TimeUnit.MILLISECONDS.sleep(10);
        Thread t2 = new Thread(booleanLockTest::syncMethodTimeoutable,"T2");
        t2.start();
        TimeUnit.MILLISECONDS.sleep(10);*/
    }
}

T2线程试图休眠10s可是在T2开始10ms时被interrupt方法打断。

结果:
在这里插入图片描述

四、阻塞的线程可超时

打开(3)处注释

package p96_boolean_lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.IntStream;

import static java.lang.Thread.currentThread;
import static java.util.concurrent.ThreadLocalRandom.current;

/**
 * @ClassName BooleanLockTest
 * @Description TODO
 * (1) 多个线程通过lock()方法争抢锁
 * (2) 可中断被阻塞的线程
 * @Author Cays
 * @Date 2019/5/3 16:54
 * @Version 1.0
 **/
public class BooleanLockTest {
    // 1. 定义BooleanLock
    private final Lock lock=new BooleanLock();
    // 2. 使用try/finally语句确保lock每次都能被正确释放
    public void syncMethod(){
        try{
            //加锁
            lock.lock();
            int randomInt = 10;
            System.out.println(currentThread()+" get the lock.");
            //休眠10s
            TimeUnit.SECONDS.sleep(randomInt);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            //释放锁
            lock.unlock();
        }
    }
    public void syncMethodTimeoutable(){
        try{
            lock.lock(1000);
            System.out.println(currentThread()+" get the lock.");
            int randomInt = 10; //current().nextInt(10);
            //休眠10s
            TimeUnit.SECONDS.sleep(randomInt);
        }catch (InterruptedException | TimeoutException e){
            //System.out.println("超时");
            e.printStackTrace();
        }finally {
            //System.out.println("超时2");
            lock.unlock();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        BooleanLockTest booleanLockTest=new BooleanLockTest();
        // 3. 定义一个线程并启动
        //(1) 多个线程通过lock()方法争抢锁
        /*IntStream.range(0,10)
                .mapToObj(i->new Thread(booleanLockTest::syncMethod))
                .forEach(Thread::start);*/
        /**
         * (2) 可中断被阻塞的线程
         */
        //new Thread(booleanLockTest::syncMethod,"T1").start();
        //TimeUnit.MILLISECONDS.sleep(2);
        /*Thread t2=new Thread(booleanLockTest::syncMethod,"T2");
        t2.start();
        TimeUnit.MILLISECONDS.sleep(10);
        t2.interrupt();
        TimeUnit.MILLISECONDS.sleep(10);
        System.out.println("T2 state:"+t2.getState());
        System.out.println("T2 interrupt:"+t2.isInterrupted());*/
        /**
         * (3) 阻塞的线程可超时
         */
        new Thread(booleanLockTest::syncMethodTimeoutable,"T1").start();
        TimeUnit.MILLISECONDS.sleep(10);
        Thread t2 = new Thread(booleanLockTest::syncMethodTimeoutable,"T2");
        t2.start();
        TimeUnit.MILLISECONDS.sleep(10);
    }
}

T1线程休眠10s期间,线程T2试图获取monitor的锁,一秒后失败,超时,T2结束生命周期;T1线程结束后直接结束。

结果:
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值