资讯专栏INFORMATION COLUMN

(五)java多线程之Lock类

googollee / 2552人阅读

摘要:本人邮箱欢迎转载转载请注明网址代码已经全部托管有需要的同学自行下载理论主要提供更多锁的特性让线程能获取同步方法或同步块的执行它们提供更多的灵活的结果能拥有不多的属性并且可以配合类提供多样的组合一个是控制多线程去访问一个共享的资源一般来说一个

本人邮箱:
欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代码已经全部托管github有需要的同学自行下载

理论

java.util.concurrent.locks.Lock: Lock主要提供更多锁的特性让线程能获取同步方法或同步块的执行.它们提供更多的灵活的结果,能拥有不多的属性,并且可以配合Condition类提供多样的组合.
一个Lock是控制多线程去访问一个共享的资源.一般来说,一个lock会提供更高级的方法去访问共享资源:比如在某一个时间点,只能有一个线程获得lock,那么这个这个lock就能访问所有的共享资源.然而,有一种lock允许多个线程同时访问共享资源,这种lock就是读写锁ReadWriteLock.

一般使用synchronized的同步方法或同步块对每个对象提供的绝对的锁去访问它们, 但也是所有的锁的请求或释放都发生阻塞.当多线程访问共享资料,只要有一个锁获取了请求,那么其他锁则必须被释放掉.

synchronized的同步方法或同步块中,若使用监视锁会是编程更加简单.它已经帮助我们把在访问锁的时候屏蔽掉一下程序公共的错误.而且使我们能更加灵活用锁进行多线程操作.

一般lock的用法如下:

Lock l = ...;
l.lock();
try{
    
}finally(
    l.unlock();
)

这是为了确保我们在使用的时候,如果执行方法出现异常,也能正常的释放锁.

编码

讲了那么多还是通过编码来学习感觉更加实际点.我们还是用之前那个售票系统

票类 Ticket

public class Ticket {
    private static final int DEFAULT_TICKET_COUNT = 1000;
    private int count = DEFAULT_TICKET_COUNT; //票的总数
    private int buyedCount = 0;
    private Object o = new Object();
    public  boolean buyTicket(int count) throws InterruptedException {

        if (this.count - count < 0){
            Thread.sleep(10);
            return false;
        }else{
            this.count = this.count - count;
            Thread.sleep(1);
            this.buyedCount = this.buyedCount + count;
            return true;
        }

    }

    public int getCount() {
        return count;
    }

    public int getBuyedCount() {
        return buyedCount;
    }

    public int getAllCount(){
        return count + buyedCount;
    }
}

然后再写一个售票类 TicketRunnable

public class TicketRunnable implements Runnable{
    private Ticket ticket;
    private Random random;
    private Lock lock;
    public TicketRunnable(Ticket ticket, Lock lock) {
        this.ticket = ticket;
        this.lock = lock;
        random = new Random();
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i ++) {
            lock.lock();
            try {
                int count = random.nextInt(10) + 1;
                boolean success = ticket.buyTicket(count);
                System.out.println(String.format("%s打算买%d张票,买票%s了,还剩下%d张票,总共卖掉%d张票, 总票数%d",
                        Thread.currentThread().getName(), count, success ? "成功" : "失败",
                        ticket.getCount(), ticket.getBuyedCount(), ticket.getAllCount()));
                if (!success) {
                    break;
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

然后写个程序测试一下

public class SyncThreadTest {

    public static void main(String[] args) throws InterruptedException {
        List threads = new ArrayList<>();
        Ticket ticket = new Ticket();
        Lock lock = new ReentrantLock();
        for (int i = 0; i < 20; i ++){
            threads.add(new Thread(new TicketRunnable(ticket, lock)));
        }

        for (Thread thread : threads){
            thread.start();
        }
    }
}

运行一下程序,截取部分输出

Thread-7打算买8张票,买票成功了,还剩下654张票,总共卖掉346张票, 总票数1000
Thread-8打算买1张票,买票成功了,还剩下653张票,总共卖掉347张票, 总票数1000
Thread-9打算买5张票,买票成功了,还剩下648张票,总共卖掉352张票, 总票数1000
Thread-10打算买10张票,买票成功了,还剩下638张票,总共卖掉362张票, 总票数1000
Thread-11打算买3张票,买票成功了,还剩下635张票,总共卖掉365张票, 总票数1000
Thread-12打算买2张票,买票成功了,还剩下633张票,总共卖掉367张票, 总票数1000
Thread-13打算买10张票,买票成功了,还剩下623张票,总共卖掉377张票, 总票数1000
Thread-14打算买5张票,买票成功了,还剩下618张票,总共卖掉382张票, 总票数1000
Thread-15打算买10张票,买票成功了,还剩下608张票,总共卖掉392张票, 总票数1000
Thread-16打算买2张票,买票成功了,还剩下606张票,总共卖掉394张票, 总票数1000
Thread-17打算买2张票,买票成功了,还剩下604张票,总共卖掉396张票, 总票数1000
Thread-18打算买1张票,买票成功了,还剩下603张票,总共卖掉397张票, 总票数1000
Thread-19打算买6张票,买票成功了,还剩下597张票,总共卖掉403张票, 总票数1000
Thread-0打算买8张票,买票成功了,还剩下589张票,总共卖掉411张票, 总票数1000
Thread-1打算买2张票,买票成功了,还剩下587张票,总共卖掉413张票, 总票数1000
Thread-2打算买8张票,买票成功了,还剩下579张票,总共卖掉421张票, 总票数1000
Thread-3打算买5张票,买票成功了,还剩下574张票,总共卖掉426张票, 总票数1000
Thread-4打算买6张票,买票成功了,还剩下568张票,总共卖掉432张票, 总票数1000
Thread-5打算买1张票,买票成功了,还剩下567张票,总共卖掉433张票, 总票数1000
Thread-6打算买3张票,买票成功了,还剩下564张票,总共卖掉436张票, 总票数1000
Thread-7打算买1张票,买票成功了,还剩下563张票,总共卖掉437张票, 总票数1000
Thread-8打算买5张票,买票成功了,还剩下558张票,总共卖掉442张票, 总票数1000
Thread-9打算买8张票,买票成功了,还剩下550张票,总共卖掉450张票, 总票数1000
Thread-10打算买4张票,买票成功了,还剩下546张票,总共卖掉454张票, 总票数1000
Thread-11打算买5张票,买票成功了,还剩下541张票,总共卖掉459张票, 总票数1000
Thread-12打算买6张票,买票成功了,还剩下535张票,总共卖掉465张票, 总票数1000
Thread-13打算买1张票,买票成功了,还剩下534张票,总共卖掉466张票, 总票数1000
Thread-14打算买8张票,买票成功了,还剩下526张票,总共卖掉474张票, 总票数1000
Thread-15打算买2张票,买票成功了,还剩下524张票,总共卖掉476张票, 总票数1000
Thread-16打算买10张票,买票成功了,还剩下514张票,总共卖掉486张票, 总票数1000

发现运行结果都是正确的

现在尝试使用lock.tryLock()方法.不管能不能获得锁,每个线程都必须卖出5次票,修改TicketRunnable

public class TicketRunnable implements Runnable{
    private Ticket ticket;
    private Random random;
    private Lock lock;
    public TicketRunnable(Ticket ticket, Lock lock) {
        this.ticket = ticket;
        this.lock = lock;
        random = new Random();
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; ) {

            if(lock.tryLock()){
                try {
                    int count = random.nextInt(10) + 1;
                    boolean success = ticket.buyTicket(count);
                    System.out.println(String.format("%s打算买%d张票,买票%s了,还剩下%d张票,总共卖掉%d张票, 总票数%d",
                            Thread.currentThread().getName(), count, success ? "成功" : "失败",
                            ticket.getCount(), ticket.getBuyedCount(), ticket.getAllCount()));
                    if (!success) {
                        break;
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                    i ++;
                }
            }else{
                System.out.println(Thread.currentThread().getName() + " 买票系统被占用,尝试获取锁失败.");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

截取部分运行结果

Thread-11打算买7张票,买票成功了,还剩下613张票,总共卖掉387张票, 总票数1000
Thread-11打算买2张票,买票成功了,还剩下611张票,总共卖掉389张票, 总票数1000
Thread-11打算买7张票,买票成功了,还剩下604张票,总共卖掉396张票, 总票数1000
Thread-11打算买3张票,买票成功了,还剩下601张票,总共卖掉399张票, 总票数1000
Thread-11打算买4张票,买票成功了,还剩下597张票,总共卖掉403张票, 总票数1000
Thread-12 买票系统被占用,尝试获取锁失败.
Thread-10 买票系统被占用,尝试获取锁失败.
Thread-16 买票系统被占用,尝试获取锁失败.
Thread-19打算买2张票,买票成功了,还剩下595张票,总共卖掉405张票, 总票数1000
Thread-19打算买8张票,买票成功了,还剩下587张票,总共卖掉413张票, 总票数1000
Thread-19打算买1张票,买票成功了,还剩下586张票,总共卖掉414张票, 总票数1000
Thread-19打算买5张票,买票成功了,还剩下581张票,总共卖掉419张票, 总票数1000
Thread-19打算买1张票,买票成功了,还剩下580张票,总共卖掉420张票, 总票数1000

发现程序也是正常的.

原理

Lock.lock() 当前线程尝试获取一个锁,如果这个锁获取不到,则当前线程会一直休眠直到获取这个锁.

Lock.lockInterruptibly() 让当前线程获取一个锁,如果锁可以用,则直接返回.否则当前线程会一直休眠直到一下两种情况中的其中一个发生:

当前线程获取到这个锁

其他线程打断当前线程, 打断当前线程获取锁的操作是允许的.

Lock.tryLock() 尝试获得一个锁,如果锁是可用的,则直接返回ture,并获取到这个锁.否则,直接返回false

Lock.tryLock(long time, TimeUnit unit) 在某一段时间内尝试获取一个锁,如果锁可用,则直接返回true,否则等待超时返回fasle

Lock.unlock() 释放锁

Lock.newCondition() 这个后面的章节再谈论

打赏

如果觉得我的文章写的还过得去的话,有钱就捧个钱场,没钱给我捧个人场(帮我点赞或推荐一下)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67059.html

相关文章

  • Java线程进阶()—— J.U.Clocks框架:LockSupport

    摘要:初始时,为,当调用方法时,线程的加,当调用方法时,如果为,则调用线程进入阻塞状态。该对象一般供监视诊断工具确定线程受阻塞的原因时使用。 showImg(https://segmentfault.com/img/remote/1460000016012503); 本文首发于一世流云的专栏:https://segmentfault.com/blog... 一、LockSupport类简介...

    jsyzchen 评论0 收藏0
  • (七)java线程Condition

    摘要:本人邮箱欢迎转载转载请注明网址代码已经全部托管有需要的同学自行下载引言在写五多线程之类时我们暂时忽略掉的一个方法那就是这个方法返回一个现在我们这章就重点讲这个东东是什么以及怎么使用理论的中文翻译是状态没错这个就是让多线程在不同状态切换其他线 本人邮箱: 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kcogithub: https://github...

    Cc_2011 评论0 收藏0
  • 深入理解Java内存模型()——锁

    摘要:前情提要深入理解内存模型四锁的释放获取建立的关系锁是并发编程中最重要的同步机制。锁内存语义的实现本文将借助的源代码,来分析锁内存语义的具体实现机制。请看下篇深入理解内存模型六 前情提要 深入理解Java内存模型(四)—— volatile 锁的释放-获取建立的happens before 关系 锁是java并发编程中最重要的同步机制。锁除了让临界区互斥执行外,还可以让释放锁的线程向...

    caige 评论0 收藏0
  • Java线程进阶(七)—— J.U.Clocks框架:AQS独占功能剖析(2)

    摘要:开始获取锁终于轮到出场了,的调用过程和完全一样,同样拿不到锁,然后加入到等待队列队尾然后,在阻塞前需要把前驱结点的状态置为,以确保将来可以被唤醒至此,的执行也暂告一段落了安心得在等待队列中睡觉。 showImg(https://segmentfault.com/img/remote/1460000016012467); 本文首发于一世流云的专栏:https://segmentfault...

    JayChen 评论0 收藏0
  • Java线程进阶(三)—— J.U.Clocks框架:ReentrantLock

    摘要:公平策略在多个线程争用锁的情况下,公平策略倾向于将访问权授予等待时间最长的线程。使用方式的典型调用方式如下二类原理的源码非常简单,它通过内部类实现了框架,接口的实现仅仅是对的的简单封装,参见原理多线程进阶七锁框架独占功能剖析 showImg(https://segmentfault.com/img/remote/1460000016012582); 本文首发于一世流云的专栏:https...

    jasperyang 评论0 收藏0

发表评论

0条评论

googollee

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<