• java 多线程 3 synchronized 同步


    多任务编程的难点在于多任务共享资源。对于同一个进程空间中的多个线程来说,它们都共享堆中的对象。某个线程对对象的操作,将影响到其它的线程。

    在多线程编程中,要尽力避免竞争条件(racing condition),即运行结果依赖于不同线程执行的先后。线程是并发执行的,无法确定线程的先后,所以我们的程序中不应该出现竞争条件。

    然而,当多任务共享资源时,就很容易造成竞争条件。我们需要将共享资源,并造成竞争条件的多个线程线性化执行,即同一时间只允许一个线程执行。

    (可更多参考Linux多线程与同步)

    下面是一个售票程序。3个售票亭(Booth)共同售卖100张票(Reservoir)。每个售票亭要先判断是否有余票,然后再卖出一张票。如果只剩下一张票,在一个售票亭的判断和售出两个动作之间,另一个售票亭卖出该票,那么第一个售票亭(由于已经执行过判断)依然会齿形卖出,造成票的超卖。为了解决该问题,判断和售出两个动作之间不能有“空隙”。也就是说,在一个线程完成了这两个动作之后,才能有另一个线程执行。

    在Java中,我们将共享的资源置于一个对象中,比如下面r(Reservoir)对象。它包含了总共的票数;将可能造成竞争条件的,针对共享资源的操作,放在synchronized(同步)方法中,比如下面的sellTicket()。synchronized是方法的修饰符。在Java中,同一对象的synchronized方法只能同时被一个线程调用。其他线程必须等待该线程调用结束,(余下的线程之一)才能运行。这样,我们就排除了竞争条件的可能。

    在main()方法中,我们将共享的资源(r对象)传递给多个线程:

    public class Test
    {
        public static void main(String[] args)
        {
            Reservoir r = new Reservoir(100);
            Booth b1 = new Booth(r);
            Booth b2 = new Booth(r);
            Booth b3 = new Booth(r);
        }
    }
    
    /**
     * contain shared resource
     */
    class Reservoir {
        private int total;
    
        public Reservoir(int t) 
        {
            this.total = t;
        }
    
        /**
         * Thread safe method
         * serialized access to Booth.total
         */
        public synchronized boolean sellTicket() 
        {
            if(this.total > 0) {
                this.total = this.total - 1;
                return true; // successfully sell one
            }
            else {
                return false; // no more tickets
            }
        }
    }
    
    /**
     * create new thread by inheriting Thread
     */
    class Booth extends Thread {
        private static int threadID = 0; // owned by Class object
    
        private Reservoir release;      // sell this reservoir 
        private int count = 0;          // owned by this thread object
        /**
         * constructor
         */
        public Booth(Reservoir r) {
            super("ID:" + (++threadID));
            this.release = r;          // all threads share the same reservoir
            this.start();
        }
    
        /**
         * convert object to string
         */
        public String toString() {
            return super.getName();
        }
    
        /**
         * what does the thread do?
         */
        public void run() {
            while(true) {
                if(this.release.sellTicket()) {
                    this.count = this.count + 1;
                    System.out.println(this.getName() + ": sell 1");
                    try {
                        sleep((int) Math.random()*100);   // random intervals
                    }
                    catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                else {
                    break;
                }
            }
            System.out.println(this.getName() + " I sold:" + count);
        }
    }
    

      Java的每个对象都自动包含有一个用于支持同步的计数器,记录synchronized方法的调用次数。线程获得该计数器,计数器加1,并执行synchronized方法。如果方法内部进一步调用了该对象的其他synchronized方法,计数器加1。当synchronized方法调用结束并退出时,计数器减1。其他线程如果也调用了同一对象的synchronized方法,必须等待该计数器变为0,才能锁定该计数器,开始执行。Java中的类同样也是对象(Class类对象)。Class类对象也包含有计数器,用于同步。

  • 相关阅读:
    【git hub使用】
    【struct2 第一天】
    【JSP基础 第一天】
    【Java基础学习 day01】
    网站建设 【Django】 【MTV】
    Python-Json字符串和XML解析
    Python-冒泡和快排
    Python-面向对象编程
    练习-字符串编码
    练习-统计文件中单词数量
  • 原文地址:https://www.cnblogs.com/alamZ/p/7729830.html
Copyright © 2020-2023  润新知