`
kobe学java
  • 浏览: 257959 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

用ReentrantLock模拟宴会的热闹情景

 
阅读更多

 

一个简单的ReentrantLock的例子, 情景是几个朋友吃饭, 可是美味的汤只有一锅, 勺子只有一个. 这样一来, 难免就会有你抢我争的情况了. 但是, 如果有更美味的其他食物, 当然可以先转头去找其他的了. synchronized是无法做到这点的.

Lunch类, 包括勺子(ReentrantLock)和"舀"的动作, 当这帮朋友想要"舀"的时候, 就只能一个人动手, 其他人乖乖等着, 或者被叫去干其他事情
package concurrent.lunch;
import java.util.concurrent.locks.ReentrantLock;

public class Lunch {
    private ReentrantLock scoop = new ReentrantLock();

    public boolean dip() {
        try {
            scoop.lockInterruptibly();//如果当前线程没有被中断 就获取锁
        } catch (InterruptedException e) {
            Logger.log("Some one call me for better food ^^ ");
            return false;
        }

        Logger.log("hah, I got the scoop");
        try {
            // suppose we need 5s to dip the soup
            try {
                Thread.sleep(5000);
            } catch (InterruptedException i) {
                Logger.log("someone rob my scoop, 55~~~ ");
                return false;
            }
            Logger.log("I got delicious food ");
        } finally {
            scoop.unlock();
        }

        return true;
    }
}


Buddy类, 嘴馋的家伙, 抢着要"舀"汤, 不过如果抢不到, 也可以干别的
package concurrent.lunch;
public class Buddy extends Thread {
    private final Lunch lunch;
    public Buddy(Lunch lunch, String name) {
        this.lunch = lunch;
        this.setName(name);
    }
    public void run() {
        while (!lunch.dip()) {
            Logger.log("I will wait for a while to dip, see if any other better...");
            try {
                Thread.sleep(100);
            } catch (InterruptedException ignore) {}
        }
    }
}


Party类, 宴会开始了, 每个人都想去抢那个勺子, 抢不到的又实在等不耐烦的话就只好暴力解决(interrupt)
package concurrent.lunch;
public class Party {
    public static void main(String[] args) throws Exception {
        // here we have to share the Lunch instance
        Lunch lunch = new Lunch();
        
        // here we MUST share the Lunch instance
        Buddy Tityz= new Buddy(lunch, "Tityz");
        Buddy Michael = new Buddy(lunch, "Michael");
        Buddy Yutting= new Buddy(lunch, "Yutting");

        Tityz.start();
        Thread.sleep(100); //make sure Tityz got it first
        Michael.start();
        Yutting.start();
        
        Thread.sleep(1000);

        // why still hanging? rob him
        Tityz.interrupt();

        // ask michael to other food
        Michael.interrupt();
    }
}


Logger:
package concurrent.lunch;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Logger {
    private static final SimpleDateFormat df = new SimpleDateFormat("hh:mm:ss");
    public static void log(String msg) {
        System.out.println(df.format(new Date()) + " "
                + Thread.currentThread().getName() + ":\t" +msg );
    }
}


好了, 开始宴会吧.
02:42:24 Tityz:        hah, I got the scoop
02:42:25 Tityz:          someone rob my scoop, 55~~~ 
02:42:25 Michael:    Some one call me for better food ^^ 
02:42:25 Yutting:     hah, I got the scoop
02:42:25 Michael:    I will wait for a while to dip, see if any other better...
02:42:25 Tityz:          I will wait for a while to dip, see if any other better...
02:42:30 Yutting:     I got delicious food 
02:42:30 Tityz:          hah, I got the scoop
02:42:35 Tityz:          I got delicious food 
02:42:35 Michael:    hah, I got the scoop
02:42:40 Michael:    I got delicious food 

结果显而易见, 首先是Tityz拿到勺子, 但是因为"舀"比较花时间, 其他人都只能等. 不过一段时间(1s)过去了, 其他人忍不住了, "抢"了他的勺子, 而Michael则被叫去做其他事情了. 得渔利者Yutting也. Yutting搞定后, Tityz再次抢到勺子(这里的次序是随机的), 这次没人打断他了, Michael则最后才喝到汤.

例子完了, 但是我们应该考虑到问题是, 这个锁定, 到底锁定的对象是什么? ReentrantLock.lock()没有参数, 不想synchronized(xx)可以指定被锁定的对象. 那么我们只能假设ReentrantLock.lock()维护了内部的对象. 显然, 如果我们new了好几个ReentrantLock实例并且每个线程分别持有一个, 那么这些线程最终获取的锁定的对象就不是同一个. 这就是上面例子的Party里共用一个ReentrantLock的原因. 

当然, 共用的形式不一定就是通过直接传递ReentrantLock对象给某个线程, 也可以是在线程执行的方法去共用ReentrantLock, 自己发挥想象力吧

同时也说一下上面代码的缺点, 留意一下上面代码Lunch.dip()的方法签名, public boolean dip(), 是带有返回值的, 这样的做法可谓喜忧参半, 好的一面是, 如果失败了, 调用该方法的线程有机会进行其他事情的处理, 不利的一面是调用该方法的线程被逼要使用不断尝试的方式来处理, 增加了代码复杂度.

我们有一种解决上述问题的做法, 就是让调用的线程等待, 直到条件满足为止. 可以参考java.util.concurrent.locks.Condition类的使用例子.
分享到:
评论

相关推荐

    Java并发之ReentrantLock类源码解析

    ReentrantLock的lock方法使用CAS操作来尝试更新对象内的一个变量,如果更新成功,则将exclusiveOwnerThread变量设置为当前线程,然后lock方法会立刻返回。如果更新不成功,则调用acquire(1)方法。 ReentrantLock的...

    Lock、Synchoronized和ReentrantLock的使用

    Lock、Synchronized 和 ReentrantLock 的使用 Lock、Synchronized 和 ReentrantLock 是 Java 中三种常用的同步机制,每种机制都有其特点和使用场景。下面对这三种机制进行详细的分析和比较。 一、Synchronized ...

    ReentrantLock的使用及注意事项

    ReentrantLock的使用及注意事项

    java ReentrantLock详解.docx

    `ReentrantLock`是Java并发编程中的一种高级锁机制,它是`java.util.concurrent.locks`包中的类,提供了比`...了解并熟练使用`ReentrantLock`能帮助开发者更好地解决并发问题,提高程序的并发性能和健壮性。

    CyclicBarrier,reentrantlock,condition模拟抢票

    用CyclicBarrier,reentrantlock,condition来完成同时购买,同步购买的功能 JUC系列之模拟抢票(N人同时抢票,票不足系统补仓,N-M人继续抢票) http://blog.csdn.net/crazyzxljing0621/article/details/77891620

    ReentrantLock源码的使用问题详解.docx

    《ReentrantLock源码详解与应用》 ReentrantLock,可重入锁,是Java并发编程中一个重要的锁实现,它提供了比synchronized更高级别的控制能力,包括公平性和非公平性选择。本文将深入探讨ReentrantLock的原理,特别...

    ReentrantLock解析

    《ReentrantLock深度解析》 在Java并发编程中,ReentrantLock是JDK提供的一个可...在实际开发中,可以根据需求选择使用synchronized还是ReentrantLock,或者其他的并发控制手段,以达到最佳的并发性能和资源利用效率。

    使用ReentrantLock和Lambda表达式让同步更

    本篇文章将深入探讨`ReentrantLock`的使用以及如何结合Lambda表达式来优化同步代码。 `ReentrantLock`是Java并发包`java.util.concurrent.locks`中的一个类,它是可重入的互斥锁,具备与`synchronized`相同的基本...

    ReentrantLock与synchronized区别

    java语言 并发编程 ReentrantLock与synchronized区别 详解

    ReentrantLock与synchronized

    在Java多线程编程中,`ReentrantLock`和`synchronized`都是用于实现线程同步的重要工具,确保在并发环境中数据的一致性和正确性。两者虽然都能实现互斥访问,但在功能、性能以及使用场景上有所不同。下面我们将深入...

    ReentrantLock源码分析

    ### ReentrantLock源码分析 #### 一、ReentrantLock简介 ReentrantLock是一个基于`AbstractQueuedSynchronizer`(AQS)实现的高级锁工具类。与传统的synchronized关键字相比,ReentrantLock提供了更多控制手段,比如...

    Java多线程之ReentrantLock与Condition - 平凡希 - 博客园1

    本篇文章将深入探讨`ReentrantLock`的特性,功能以及如何使用。 ### 1. ReentrantLock简介 `ReentrantLock`是Java并发编程中的一种重要工具,它允许线程在已经获取锁的情况下再次获得同一把锁,而不必等待解锁。这...

    ReentrantLock 实现原理 1

    默认情况下,ReentrantLock 使用非公平锁,它的效率和吞吐量都比公fair锁高的多。 获取锁的过程中,ReentrantLock 使用了 AQS 的 acquiring 机制。首先,ReentrantLock 会调用 sync 的 lock 方法,而这个方法是一个...

    一张图将整个ReentrantLock流程看懂

    一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个ReentrantLock流程看懂,干货满满 一张图将整个...

    深入java并发编程,使用ReentrantLock和 Synchronized加锁

    在Java并发编程中,理解和熟练使用同步机制是至关重要的,这包括了`ReentrantLock`和`synchronized`关键字。这两个工具都是为了确保多线程环境中的数据一致性与安全性,防止出现竞态条件和死锁等问题。 `...

    ReentrantLock代码剖析之ReentrantLock_lock

    默认情况下,`ReentrantLock`使用的是非公平锁,因为非公平锁在大多数情况下具有更高的吞吐量。 `Sync`类继承自`AbstractQueuedSynchronizer`(AQS),这是一个非常关键的抽象类,它提供了锁和其他同步组件的基础框架...

    Java中ReentrantLock的使用.docx

    Java中的ReentrantLock是线程安全编程中的一种高级锁机制,它属于Lock接口的一个实现,提供了比synchronized更丰富的功能和更高的灵活性。ReentrantLock的名字来源于它的可重入性,这意味着一个线程可以多次获取同一...

    ReentrantLock 与 synchronized 简介

    - 使用`ReentrantLock`时,必须显式地在`finally`块中释放锁,以防止由于异常而导致的锁未释放问题。 - `ReentrantLock`支持重入特性,即允许同一个线程多次获取同一把锁,这与`synchronized`的行为一致。 #### ...

    ReentrantLock lock方法注释

    ReentrantLock lock方法注释

    教你完全理解ReentrantLock重入锁

    ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞。在java关键字synchronized隐式支持重...

Global site tag (gtag.js) - Google Analytics