• Java学习-078-多线程11:使用 synchronized 同步代码块解决线程资源同步问题


    多线程资源同步问题,可通过 synchronized (同步代码块)来解决,同步代码块使用方法如下所示:

    synchronized (同步对象) {
         // 同步代码块,对同步对象共享资源的操作
    }

      

    将操作线程同一共享资源的代码作为同步代码块包含在 synchronized 同步代码块中,即可解决多线程的资源同步问题。具体实例源代码如下所示:

    package com.fanfengping.demo;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class Demo02SaleTicketRunnable implements Runnable {
        private int stockTicket;
        private int saleTicket;
    
        Demo02SaleTicketRunnable() {
            stockTicket = 20;
            saleTicket = 0;
        }
    
        @Override
        public void run() {
            log.info("Running Runnable Thread : {}", Thread.currentThread().getName());
    
            while(stockTicket > 0) {
                synchronized (this) {
                    try {
                        if (stockTicket > 0) {
                            log.info("Runnable {}:已售票数 {},剩余票数 {}{}", Thread.currentThread().getName(), ++saleTicket, --stockTicket, stockTicket == 0 ? ",停止售票!" : "");
                        } else {
                            log.info("Runnable {}:已售票数 {},剩余票数 {},停止售票!", Thread.currentThread().getName(), saleTicket, stockTicket);
                            break;
                        }
    
                        // 同步代码语句块里休眠,有点类似不休眠,此时sleep相当于抱着锁休眠
                        // 当sleep不释放锁时,自身不执行,线程也不执行,同时其他线程也无法获得锁,也不能执行同步代码
                        Thread.sleep(800);
                    } catch (InterruptedException e) {
                        log.info("Runnable {} interrupted!", Thread.currentThread().getName());
                        e.printStackTrace();
                    }
                }
            }
    
            log.info("Runnable {} exit", Thread.currentThread().getName());
        }
    }

      

    测试源代码如下所示:

    package com.fanfengping.demo;
    
    public class Demo02SaleTicketRunnableTest {
        public static void main(String[] args) {
            Demo02SaleTicketRunnable st = new Demo02SaleTicketRunnable();
    
            for (int j = 1; j < 6; j++) {
                new Thread(st, "售票窗口 " + j).start();
            }
        }
    }

      

    执行程序输出结果如下所示:

    [售票窗口 5] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Running Runnable Thread : 售票窗口 5
    [售票窗口 3] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Running Runnable Thread : 售票窗口 3
    [售票窗口 2] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Running Runnable Thread : 售票窗口 2
    [售票窗口 1] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Running Runnable Thread : 售票窗口 1
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Running Runnable Thread : 售票窗口 4
    [售票窗口 5] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 5:已售票数 1,剩余票数 19
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 2,剩余票数 18
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 3,剩余票数 17
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 4,剩余票数 16
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 5,剩余票数 15
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 6,剩余票数 14
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 7,剩余票数 13
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 8,剩余票数 12
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 9,剩余票数 11
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 10,剩余票数 10
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 11,剩余票数 9
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 12,剩余票数 8
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 13,剩余票数 7
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 14,剩余票数 6
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 15,剩余票数 5
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 16,剩余票数 4
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 17,剩余票数 3
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 18,剩余票数 2
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 19,剩余票数 1
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4:已售票数 20,剩余票数 0,停止售票!
    [售票窗口 4] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 4 exit
    [售票窗口 1] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 1:已售票数 20,剩余票数 0,停止售票!
    [售票窗口 1] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 1 exit
    [售票窗口 2] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 2:已售票数 20,剩余票数 0,停止售票!
    [售票窗口 2] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 2 exit
    [售票窗口 3] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 3:已售票数 20,剩余票数 0,停止售票!
    [售票窗口 3] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 3 exit
    [售票窗口 5] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 5:已售票数 20,剩余票数 0,停止售票!
    [售票窗口 5] INFO com.fanfengping.demo.Demo02SaleTicketRunnable - Runnable 售票窗口 5 exit

      

    根据程序输出可以看出,虽然售票信息正确,未出现数据不一致的情况,但各个售票窗口的售票操作较为集中在 售票窗口 4,并非离散型的销售。

    因为 Thread.sleep 也在同步代码块中,在同步代码块中进行休眠时,实际线程依然占用着同步锁,并未释放,其他活动的线程无法获取同步锁自然也无法执行。将休眠放到同步锁外面即可解决,源代码示例如下所示:

    package com.fanfengping.demo;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class Demo03SaleTicketRunnable implements Runnable {
        private int stockTicket;
        private int saleTicket;
    
        Demo03SaleTicketRunnable() {
            stockTicket = 20;
            saleTicket = 0;
        }
    
        @Override
        public void run() {
            log.info("Running Runnable Thread : {}", Thread.currentThread().getName());
    
            while(stockTicket > 0) {
                synchronized (this) {
                    if (stockTicket > 0) {
                        log.info("Runnable {}:已售票数 {},剩余票数 {}{}", Thread.currentThread().getName(), ++saleTicket, --stockTicket, stockTicket == 0 ? ",停止售票!" : "");
                    } else {
                        log.info("Runnable {}:已售票数 {},剩余票数 {},停止售票!", Thread.currentThread().getName(), saleTicket, stockTicket);
                        break;
                    }
                }
    
                // 将 sleep 放在同步代码块的外面,防止休眠时占用同步锁
                try {
                    Thread.sleep(800);
                } catch (InterruptedException e) {
                    log.info("Runnable {} interrupted!", Thread.currentThread().getName());
                    e.printStackTrace();
                }
            }
    
            log.info("Runnable {} exit", Thread.currentThread().getName());
        }
    }

    测试源代码如下所示:

    package com.fanfengping.demo;
    
    public class Demo03SaleTicketRunnableTest {
        public static void main(String[] args) {
            Demo03SaleTicketRunnable st = new Demo03SaleTicketRunnable();
    
            for (int j = 1; j < 6; j++) {
                new Thread(st, "售票窗口 " + j).start();
            }
        }
    }

      

    执行程序输出结果如下所示:

    [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Running Runnable Thread : 售票窗口 4
    [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Running Runnable Thread : 售票窗口 2
    [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Running Runnable Thread : 售票窗口 3
    [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Running Runnable Thread : 售票窗口 1
    [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Running Runnable Thread : 售票窗口 5
    [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 4:已售票数 1,剩余票数 19
    [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 5:已售票数 2,剩余票数 18
    [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 1:已售票数 3,剩余票数 17
    [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 3:已售票数 4,剩余票数 16
    [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 2:已售票数 5,剩余票数 15
    [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 5:已售票数 6,剩余票数 14
    [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 3:已售票数 7,剩余票数 13
    [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 2:已售票数 8,剩余票数 12
    [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 4:已售票数 9,剩余票数 11
    [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 1:已售票数 10,剩余票数 10
    [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 1:已售票数 11,剩余票数 9
    [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 4:已售票数 12,剩余票数 8
    [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 3:已售票数 13,剩余票数 7
    [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 2:已售票数 14,剩余票数 6
    [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 5:已售票数 15,剩余票数 5
    [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 5:已售票数 16,剩余票数 4
    [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 1:已售票数 17,剩余票数 3
    [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 4:已售票数 18,剩余票数 2
    [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 2:已售票数 19,剩余票数 1
    [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 3:已售票数 20,剩余票数 0,停止售票!
    [售票窗口 3] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 3 exit
    [售票窗口 1] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 1 exit
    [售票窗口 5] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 5 exit
    [售票窗口 4] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 4 exit
    [售票窗口 2] INFO com.fanfengping.demo.Demo03SaleTicketRunnable - Runnable 售票窗口 2 exit

    当然也可将同步的代码块单独放到一个方法中,然后在 run 方法中调用,源代码示例如下所示:

    package com.fanfengping.demo;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class Demo04SaleTicketRunnable implements Runnable {
        private int stockTicket;
        private int saleTicket;
    
        Demo04SaleTicketRunnable() {
            stockTicket = 20;
            saleTicket = 0;
        }
    
        private void sellTicket() {
            synchronized (this) {
                if (stockTicket > 0) {
                    log.info("Runnable {}:已售票数 {},剩余票数 {}{}", Thread.currentThread().getName(), ++saleTicket, --stockTicket, stockTicket == 0 ? ",停止售票!" : "");
                } else {
                    log.info("Runnable {}:已售票数 {},剩余票数 {},停止售票!", Thread.currentThread().getName(), saleTicket, stockTicket);
                }
            }
        }
    
        @Override
        public void run() {
            log.info("Running Runnable Thread : {}", Thread.currentThread().getName());
    
            while(stockTicket > 0) {
                sellTicket();
                try {
                    // 将 sleep 放在同步代码块外,在售票完成后即释放同步锁,并休眠一会,允许其他窗口继续售票
                    Thread.sleep(800);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            log.info("Runnable {} exit", Thread.currentThread().getName());
        }
    }

    测试源代码如下所示:

    package com.fanfengping.demo;
    
    public class Demo04SaleTicketRunnableTest {
        public static void main(String[] args) {
            Demo04SaleTicketRunnable st = new Demo04SaleTicketRunnable();
    
            for (int j = 1; j < 6; j++) {
                new Thread(st, "售票窗口 " + j).start();
            }
        }
    }

    执行程序输出结果如下所示:

    [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Running Runnable Thread : 售票窗口 2
    [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Running Runnable Thread : 售票窗口 1
    [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Running Runnable Thread : 售票窗口 5
    [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Running Runnable Thread : 售票窗口 3
    [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 2:已售票数 1,剩余票数 19
    [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Running Runnable Thread : 售票窗口 4
    [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 3:已售票数 2,剩余票数 18
    [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 4:已售票数 3,剩余票数 17
    [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 5:已售票数 4,剩余票数 16
    [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 1:已售票数 5,剩余票数 15
    [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 3:已售票数 6,剩余票数 14
    [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 4:已售票数 7,剩余票数 13
    [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 2:已售票数 8,剩余票数 12
    [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 1:已售票数 9,剩余票数 11
    [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 5:已售票数 10,剩余票数 10
    [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 2:已售票数 11,剩余票数 9
    [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 4:已售票数 12,剩余票数 8
    [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 3:已售票数 13,剩余票数 7
    [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 5:已售票数 14,剩余票数 6
    [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 1:已售票数 15,剩余票数 5
    [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 2:已售票数 16,剩余票数 4
    [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 3:已售票数 17,剩余票数 3
    [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 4:已售票数 18,剩余票数 2
    [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 5:已售票数 19,剩余票数 1
    [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 1:已售票数 20,剩余票数 0,停止售票!
    [售票窗口 2] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 2 exit
    [售票窗口 3] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 3 exit
    [售票窗口 4] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 4 exit
    [售票窗口 1] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 1 exit
    [售票窗口 5] INFO com.fanfengping.demo.Demo04SaleTicketRunnable - Runnable 售票窗口 5 exit
    我本渺小,但山峰,我一次次绝顶!


    PS:若有错误,敬请告知,不胜感激!
    Copyright @范丰平 版权所有,如需转载请标明本文原始链接出处,严禁商业用途! 我的个人博客链接地址:http://www.cnblogs.com/fengpingfan
  • 相关阅读:
    NET性能优化方面的总结
    SVN 全局忽略列表
    Visual Studio 2005 IDE Themes
    如何编写网络监视器
    微软研究院Detour开发包之API拦截技术
    NDIS HOOK实现方法
    如何使用MAP文件找到程序崩溃时源码是哪行异常
    QT 环境变量设置
    Winsock工作模型 ( 转 )
    如何使用MAP文件找到程序崩溃的原因
  • 原文地址:https://www.cnblogs.com/fengpingfan/p/14708370.html
Copyright © 2020-2023  润新知