JAVA并发编程(二)——同步控制(上)

在并发编程中,同步控制是最重要也是最需要保障的。想象这样的场景:你去银行取了100万,余额理应还有900万,但是ATM机却告诉你,余额还剩1000万,那这多出来的100万哪里来的呢?这里我将介绍JAVA中控制线程同步最基本的方法。


同步控制基本方法

Synchronized和他的伙伴

ReentrantLock和他的女朋友们


Synchronized、wait()、notify()

Synchronized可以理解为锁,用它可以对一个共享的对象加锁,使得,每次都只有一个线程能访问到这个共享,可以理解为十个人同时要用一个浴室。wait()则是指小明取得了浴室的钥匙,洗头时,发现没带洗发水,然后,交还浴室的锁,回宿舍拿海飞丝。notify()指的是小张快洗完了,并且大喊“老子马上要洗完澡了,你们可以准备抢浴室的使用权了”。sleep()是指小二去洗澡,洗到一半,突然思考人生十分钟,什么也不干,就像俗话说的“占着茅坑不拉屎”。

并发中最经典的例子——生产者消费者,可以帮助你好好理解上面的浴室,小明,小张,小二。

class Shop{ 
    //商店就100个架子,衣服数量超过100就不能上架
    private final int MAX = 100;
    private int goodsNum = 0;
    public void buy(int num){
        //获得商店的锁
        synchronized (this) {
            while(goodsNum+num > MAX){
                System.out.println("进货:"+num+"件,太多:失败。库存:"+goodsNum);
                try {
                //等待卖货
                    wait();
                } catch (InterruptedException e) {
                    System.out.println("等待卖出被打断");
                }
            }
            goodsNum = goodsNum+num;
            System.out.println("进货:"+num+"件:成功。库存:"+goodsNum);
            //通知卖货的,我进货了,你可以卖衣服啦
            this.notifyAll();
        }
    }
    public void sale(int num){
        //获得商店的锁
        synchronized (this) {
            //卖货时发现库存不够了
            while(goodsNum-num < 0){
                System.out.println("卖出:"+num+"件,库存不够:失败。库存:"+goodsNum);
                try {
                //等待进货
                    wait();
                } catch (InterruptedException e) {
                    System.out.println("等待进货被打断");
                }
            }
            goodsNum = goodsNum-num;
            System.out.println("卖货:"+num+"件:成功。库存:"+goodsNum);
            //通知进货的,衣服卖掉了,可以进货了
            this.notifyAll();
        }
    }
}

class Buyer extends Thread{
    private Shop shop;
    private int num;
    public Buyer(Shop shop,int num){
        this.shop = shop;
        this.num = num;
        }
    public void run(){
        shop.buy(num);
    }
}
class Saler extends Thread{
    private Shop shop;
    private int num;
    public Saler(Shop shop,int num){
        this.shop = shop;
        this.num = num;
        }
    public void run(){
        shop.sale(num);
    }
}

public class MyConsAndProd {

    public static void main(String[] args) {
        Shop shop = new Shop();
        //使用Executors启动了10个消费者和生产者
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 10; i < 100; i+=10) {
            executorService.execute(new Buyer(shop, i));
            executorService.execute(new Saler(shop, i+5));
        }
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdownNow();
    }
}

OUTPUT:
进货:10件:成功。库存:10
卖出:15件,库存不够:失败。库存:10
进货:20件:成功。库存:30
卖货:15件:成功。库存:15
进货:30件:成功。库存:45
卖货:25件:成功。库存:20
进货:50件:成功。库存:70
卖货:45件:成功。库存:25
卖出:65件,库存不够:失败。库存:25

使用了Synchronized、wait()、notifyall()模拟了一家商店卖货进货的过程(画的丑,看官见谅。。。。)。
流程如下:
这里写图片描述
注意:使用wait()和notifyall()需要先获得对象的锁。


ReentrantLock、await、signal

相较于使用Synchronized,ReentrantLock多了一些功能,并且它使得代码更加具有可读性。使用方法如下:

public static ReentrantLock lock = new ReentrantLock();
@Override
    public void run() {
        for(i =0;i<100;i++){
            try {
                //对锁请求,可以响应中断的
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                i++;
            } finally{
                //释放锁
                if(lock.isHeldByCurrentThread()){
                    lock.unlock();
                }
            }
        }
    }

使用ReentrantLock虽然增加了代码的行数,但是可读性确实大大增加了。更重要的是,它多了更多的功能。

功能一:lock.lockInterruptibly();在申请锁的过程中,可以响应中断,要知道Synchronized在请求锁的过程中是不可中断的

功能二:lock.tryLock(timeout, unit);在申请锁的过程中,我们可以限定一个时间timeout和时间单位unit,告诉线程:“如果你小子一段时间内拿不到锁,那就自己中断吧,别等下去了!”而Synchronized在没有外部中断的条件下,则会傻傻的等待着,如此专一

有同学读过JDK源码可能会知道ReentrantLock中没有wait和notify方法,那么如何实现Synchronized中的wait()和notify()的功能?ReentrantLock有一个好搭档——Condition,有了它,wait()和notify()功能就有了。

Condition condition = lock.newCondition();
condition.await();//类似于wait()
condition.awaitUninterruptibly();//类似于wait()但是不会响应中断
condition.signal();//类似于notify()
condition.signalAll();//类似于notifyAll()

大家可以使用ReentrantLock对上面商店进货、卖衣服进行的过程仿真。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值