synchronized 修饰的方法无法实现互斥的问题解决

来源:互联网 发布:mac pro 2016电池容量 编辑:程序博客网 时间:2024/06/07 20:30

模拟订票场景,总共10张票,但是有20个人想要购买,所以定义了一个订票管理类专门负责操作这10张票,当然,操作票的时候使用到了synchronized,另外定义了一个调用订票方法的线程类用于模拟想要买票的人,然后创建20个线程模拟20个人来订票。但是运行测试的结果却并没有成功模拟这10张票的售卖过程,原因和处理分析如下:

订票管理类

设置 10 张票出售。

package com.example.study;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * 订票管理 *  * @author 李关钦 * @date 2017年6月7日 */public class TicketManager {    public static int ticket = 10;// 默认总共 10 张票    private static final Logger logger = LoggerFactory.getLogger(TicketManager.class);    /**     * 售票,先检查是否有票,有则对原票数减一     *      * @return     */    public synchronized boolean buyTicket() {        // 延迟 1 秒钟,模拟实际情况中的处理时间        try {            Thread.sleep(10);        } catch (InterruptedException e) {            e.printStackTrace();        }        if (ticket > 0) {            logger.info("current thread name is [{}] , before is [{}] , after is [{}]", Thread.currentThread().getName(), ticket, --ticket);            return true;        } else {            logger.info("current thread name is [{}] , ticket is [{}] , The ticket has been sold out !!!", Thread.currentThread().getName(), ticket);            return false;        }    }}

订票线程

模拟订票的人。

package com.example.study;/** * 订票线程,实现 Runnable 接口 *  * @author 李关钦 * @date 2017年6月7日 */public class BuyTicketRunnable implements Runnable {    private TicketManager ticketManager = new TicketManager();    @Override    public void run() {        ticketManager.buyTicket();    }}

测试

创建 20 个线程,模拟

package com.example.study;/** *  *  * @author 李关钦 * @date 2017年6月7日 */public class Test {    public static void main(String[] args) {        // 设置 20 个进程,模拟 20 个人订票        for (int i = 0; i < 20; i++) {            Thread thread = new Thread(new BuyTicketRunnable());            thread.start();        }    }}

结果:
这里写图片描述

从运行结果可以看到,线程 10 和线程 8 订票的结果一样了,也就说明了使用 synchronized 修饰的订票方法不起效,达不到互斥的效果。

原因:
在以上代码中,每创建一个订票线程 BuyTicketRunnabl创建一个 TicketManager 订票管理对象与之对应,也就是说创建了 20 个订票线程就会有 20 个对应的订票管理对象去操作票数,所以达不到互斥的关系。

解决方法:
在订票线程中用 static 修饰订票管理对象,使订票管理对象成为全局变量,与其他线程共享,所以 创建的 20 个线程就会共享同一个 TicketManager 订票管理对象,而 TicketManager 里面的订票方法又是使用 synchronized 的,所以就会达到互斥的效果。

package com.example.study;/** * 订票线程,实现 Runnable 接口 *  * @author 李关钦 * @date 2017年6月7日 */public class BuyTicketRunnable implements Runnable {    private static TicketManager ticketManager = new TicketManager();    @Override    public void run() {        ticketManager.buyTicket();    }}

这里写图片描述

阅读全文
0 0