学透并发编程——LeetCode多线程协作经典题

LeetCode上专门的多线程题目版块,本文通过解决几道leetCode上的多线程协作交替打印的编程题,来加深对并发编程的理解,并训练一下并发编程思想。

一、交替打印

交替打印FooBar

示例
输入: n = 2
输出: “foobarfoobar”
解释: “foobar” 将被输出两次。

方法1:synchronized锁

背景知识
1、wait()使当前线程阻塞,前提是必须先获得锁,一般配synchronized 关键字使用,即只能在synchronized同步代码块里使用wait()、notify/notifyAll()方法。

2、 由于wait()、notify/notifyAll()在synchronized 代码块执行,说明当前线程一定是获取了锁的。当线程执行wait()方法时候,会释放当前的锁,然后让出CPU,进入等待状态。只有当notify/notifyAll()被执行时候,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized代码块的代码或是中途遇到wait() ,才释放锁

3、notify/notifyAll()的执行只是唤醒沉睡的线程,而不会立即释放锁,锁的释放要看代码块的具体执行情况。所以在编程中,尽量在使用了notify/notifyAll()后立即退出临界区,以唤醒其他线程让其获得锁

4、notify和wait的顺序不能错,如果A线程先执行notify方法,B线程再执行wait方法,那么B线程是无法被唤醒的。

5、notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。notifyAll会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。如果当前情况下有多个线程需要被唤醒,推荐使用notifyAll方法。比如在生产者——消费者场景下使用,每次都需要唤醒所有的消费者或是生产者,以判断程序是否可以继续往下执行。

public class FooBarSync {

    private  int n;
    private  volatile boolean fooExec = true;

    public FooBarSync(int n) {
        this.n = n;
    }
    
    public void foo() throws InterruptedException {
        int i = 0;
        synchronized (this) {
            while (i < n) {
                if (fooExec) {
                    System.out.print("foo");
                    i++;
                    fooExec = false;
                    this.notifyAll();
                } else {
                    //foo线程等待
                    this.wait();
                }
            }
        }
    }

    public void bar() throws InterruptedException {
        int i = 0;
        synchronized (this) {
            while (i < n) {
                if (!fooExec) {
                    System.out.print("bar");
                    i++;
                    fooExec = true;
                    this.notifyAll();
                } else {
                    //bar线程等待
                    this.wait();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        FooBarSync fooBar = new FooBarSync(5);
        new Thread(()-> {
            try {
                fooBar.foo();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(()-> {
            try {
                fooBar.bar();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

方法2:ReentrantLock

背景知识
ReentrantLock主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似。CAS是一个原子操作,被广泛的应用在Java的底层实现中。在Java中,CAS主要是由sun.misc.Unsafe这个类通过JNI调用CPU底层指令实现。

ReentrantLock和synchronized都是独占锁,只允许线程互斥的访问临界区。但是实现上两者不同:synchronized加锁解锁的过程是隐式的,用户不用手动操作,优点是操作简单,但显得不够灵活。一般并发场景使用synchronized的就够了;ReentrantLock需要手动加锁和解锁,且解锁的操作尽量要放在finally代码块中,保证线程正确释放锁

public class FooBarLock {
    private  int n;
    private  ReentrantLock lock = new ReentrantLock();
    private  volatile boolean fooExec = true;

    public FooBarLock(int n) {
        this.n = n;
    }
    public void foo() throws InterruptedException {
        for (int i = 0; i < n; ) {
            lock.lock();
            try {
                if (fooExec) {
                    System.out.print("foo");
                    fooExec = false;
                    i++;
                }
            } finally {
                lock.unlock();
            }
        }
    }
    public void bar() throws InterruptedException {
        for (int i = 0; i < n; ) {
            lock.lock();
            try {
                if (!fooExec) {
                    System.out.print("bar");
                    fooExec = true;
                    i++;
                }
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        FooBarLock fooBar = new FooBarLock(5);
        new Thread(()-> {
            try {
                fooBar.foo();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        new Thread(()-> {
            try {
                fooBar.bar();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

二、按条件打印字符串

交替打印字符串
编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,但是:

如果这个数字可以被 3 整除,输出 “fizz”。
如果这个数字可以被 5 整除,输出 “buzz”。
如果这个数字可以同时被 3 和 5 整除,输出 “fizzbuzz”。
例如,当 n = 15,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz。

方法1:synchronized锁

public class FizzBuzzSync {

    private int n;

    /**
     * 定义当前数字
     */
    private int i = 1;

    public FizzBuzzSync(int n) {
        this.n = n;
    }

    //被3整除不能被5整除,输出fizz
    public void fizz() throws InterruptedException {
        synchronized (this) {
            while (i <= n) {
                if (i % 3 == 0 && i % 5 != 0) {
                    System.out.print("fizz");
                    i++;
                    this.notifyAll();
                } else {
                    //Fizz线程等待
                    this.wait();
                }
            }
        }
    }


    //被5整除不能被3整除,输出buzz
    public void buzz() throws InterruptedException {
        synchronized (this) {
            while (i <= n) {
                if (i % 3 != 0 && i % 5 == 0) {
                    System.out.print("buzz");
                    i++;
                    this.notifyAll();
                } else {
                    //Buzz线程等待
                    this.wait();
                }
            }
        }
    }

    //能被3整除能被3整除,输出fizzbuzz
    public void fizzbuzz() throws InterruptedException {
        synchronized (this) {
            while (i <= n) {
                if (i % 3 == 0 && i % 5 == 0) {
                    System.out.print("fizzbuzz");
                    i++;
                    this.notifyAll();
                } else {
                    //FizzBuzz线程等待
                    this.wait();
                }
            }
        }
    }

    //不能被3整和5整除,输出fizzbuzz
    public void number() throws InterruptedException {
        synchronized (this) {
            while (i <= n) {
                if (i % 3 != 0 && i % 5 != 0) {
                    System.out.print(i);
                    i++;
                    this.notifyAll();
                } else {
                    //Number线程等待
                    this.wait();
                }
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("Hello World!");
        FizzBuzzLock fizzBuzz = new FizzBuzzLock(15);

        new Thread(()-> {
            try {
                fizzBuzz.fizz();
            } catch (InterruptedException e) {
            }
        }).start();


        new Thread(()-> {
            try {
                fizzBuzz.buzz();
            } catch (InterruptedException e) {
            }
        }).start();


        new Thread(()-> {
            try {
                fizzBuzz.fizzbuzz();
            } catch (InterruptedException e) {
            }
        }).start();


        new Thread(()-> {
            try {
                fizzBuzz.number();
            } catch (InterruptedException e) {
            }
        }).start();
    }
}

方法2:ReentrantLock + Condition

Condition强大的地方在于它能够精确的控制多线程的休眠与唤醒(注意是唤醒,唤醒只意味着进入了同步队列,不意味着一定能获得资源),相比使用Object的wait()/notify(),使用Condition的await()/signal()这种方式能够更加安全和高效地实现线程间协作。

public class FizzBuzzLock {

    private int n;

    private ReentrantLock lock = new ReentrantLock();

    /**
     * 定义Condition
     */
    private Condition condition = lock.newCondition();

    /**
     * 定义当前数字
     */
    private int i = 1;

    public FizzBuzzLock(int n) {
        this.n = n;
    }

    //被3整除不能被5整除,输出fizz
    public void fizz() throws InterruptedException {
        lock.lock();
        try {
            while (i <= n) {
                if (i % 3 == 0 && i % 5 != 0) {
                    System.out.print("fizz");
                    i++;
                    condition.signalAll();
                } else {
                    //Fizz线程等待
                    condition.await();
                }
            }
        } finally {
            lock.unlock();
        }
    }


    //被5整除不能被3整除,输出buzz
    public void buzz() throws InterruptedException {
        lock.lock();
        try {
            while (i <= n) {
                if (i % 3 != 0 && i % 5 == 0) {
                    System.out.print("buzz");
                    i++;
                    condition.signalAll();
                } else {
                    //Buzz线程等待
                    condition.await();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    //能被3整除也能被5整除,输出fizzbuzz
    public void fizzbuzz() throws InterruptedException {
        lock.lock();
        try {
            while (i <= n) {
                if (i % 3 == 0 && i % 5 == 0) {
                    System.out.print("fizzbuzz");
                    i++;
                    condition.signalAll();
                } else {
                    //FizzBuzz线程等待
                    condition.await();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    //不能被3整和5整除,输出fizzbuzz
    public void number() throws InterruptedException {
        lock.lock();
        try {
            while (i <= n) {
                if (i % 3 != 0 && i % 5 != 0) {
                    System.out.print(i);
                    i++;
                    condition.signalAll();
                } else {
                    //Number线程等待
                    condition.await();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        System.out.println("Hello World!");
        FizzBuzzLock fizzBuzz = new FizzBuzzLock(15);

        new Thread(()-> {
            try {
                fizzBuzz.fizz();
            } catch (InterruptedException e) {
            }
        }).start();


        new Thread(()-> {
            try {
                fizzBuzz.buzz();
            } catch (InterruptedException e) {
            }
        }).start();


        new Thread(()-> {
            try {
                fizzBuzz.fizzbuzz();
            } catch (InterruptedException e) {
            }
        }).start();


        new Thread(()-> {
            try {
                fizzBuzz.number();
            } catch (InterruptedException e) {
            }
        }).start();
    }
}

三、交替打印零与奇偶数

打印零与奇偶数

示例
输入:n = 2
输出:“0102”
说明:三条线程异步执行,其中一个调用 zero(),另一个线程调用 even(),最后一个线程调用odd()。正确的输出为 “0102”。

输入:n = 5
输出:“0102030405”

方法1:ReentrantLock + Condition

Condition强大的地方在于它能够精确的控制多线程的休眠与唤醒(注意是唤醒,唤醒只意味着进入了同步队列,不意味着一定能获得资源),相比使用Object的wait()/notify(),使用Condition的await()/signal()这种方式能够更加安全和高效地实现线程间协作

public class ZeroEvenOdd {
    /**
     * 最后一个打印的数字
     */
    private int n;
    /**
     * 控制打印的标志变量
     * flag = 1表示要打印奇数了,flag = 2表示要打印偶数了,flag = 0表示打印0
     */
    private int flag = 0;
    
    private Lock lock = new ReentrantLock();
    private Condition zeroCondition = lock.newCondition();
    private Condition evenCondition = lock.newCondition();
    private Condition oddCondition = lock.newCondition();

    public ZeroEvenOdd(int n) {
        this.n = n;
    }

    // 仅打印出 0
    public void zero(IntConsumer printNumber) throws InterruptedException {
        lock.lock();
        try {
            for (int i = 1; i <= n; i++) {
                if (flag != 0) {
                    // zero线程等待
                    zeroCondition.await();
                }
                printNumber.accept(0);
                if (i % 2 == 1) {
                    flag = 1;
                    // 指定唤醒odd线程
                    oddCondition.signal();
                } else {
                    flag = 2;
                    // 指定唤醒even线程
                    evenCondition.signal();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    // 仅打印出 偶数
    public void even(IntConsumer printNumber) throws InterruptedException {
        lock.lock();
        try {
            for (int i = 2; i <= n; i += 2) {
                if (flag != 2) {
                    // even线程等待
                    evenCondition.await();
                }
                printNumber.accept(i);
                flag = 0;
                // 唤醒zero线程
                zeroCondition.signal();
            }
        } finally {
            lock.unlock();
        }
    }

    // 仅打印出 奇数
    public void odd(IntConsumer printNumber) throws InterruptedException {
        lock.lock();
        try {
            for (int i = 1; i <= n; i += 2) {
                if (flag != 1) {
                    // odd线程等待
                    oddCondition.await();
                }
                printNumber.accept(i);
                flag = 0;
                // 唤醒zero线程
                zeroCondition.signal();
            }
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
         ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(5);
         new Thread(()-> {
             try {
                 zeroEvenOdd.odd(e -> System.out.print(e));
             } catch (InterruptedException e) {
             }
         }).start();
        new Thread(()-> {
            try {
                zeroEvenOdd.zero(e -> System.out.print(e));
            } catch (InterruptedException e) {
            }
        }).start();
        new Thread(()-> {
            try {
                zeroEvenOdd.even(e -> System.out.print(e));
            } catch (InterruptedException e) {
            }
        }).start();
    }
}

方法2:CountDownLatch

背景知识
CountDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在锁上等待的线程就可以恢复工作了

//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public void await() throws InterruptedException;   
//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException;  
//将count值减1
public void countDown();  
public class ZeroEvenOdd {
    /**
     * 最后一个打印的数字
     */
    private int n;
    private CountDownLatch zeroLatch = new CountDownLatch(1);
    private CountDownLatch evenLatch = new CountDownLatch(1);
    private CountDownLatch oddLatch = new CountDownLatch(1);

    public ZeroEvenOdd(int n) {
        this.n = n;
    }
    // 仅打印出 0
    public void zero(IntConsumer printNumber) throws InterruptedException {
        for (int i = 1; i <= n; i++) {
            zeroLatch = new CountDownLatch(1);
            printNumber.accept(0);
            // 如果已经打印了奇数个0,那么接下来要打印奇数
            if ((i&1) == 1) {
                oddLatch.countDown();
            } else {
                evenLatch.countDown();
            }
            // 当前线程挂起,直到值减为0
            zeroLatch.await();
        }
    }
    // 仅打印出 偶数
    public void even(IntConsumer printNumber) throws InterruptedException {
        for (int i = 2; i <= n; i += 2) {
            evenLatch.await();
            printNumber.accept(i);
            // 接下来要打印0
            evenLatch = new CountDownLatch(1);
            zeroLatch.countDown();
        }
    }
    // 仅打印出 奇数
    public void odd(IntConsumer printNumber) throws InterruptedException {
        for (int i = 1; i <= n; i += 2) {
            oddLatch.await();
            printNumber.accept(i);
            // 接下来要打印0
            oddLatch = new CountDownLatch(1);
            zeroLatch.countDown();
        }
    }

    public static void main(String[] args) {
        ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(5);
        new Thread(()-> {
            try {
                zeroEvenOdd.odd(e -> System.out.print(e));
            } catch (InterruptedException e) {
            }
        }).start();
        new Thread(()-> {
            try {
                zeroEvenOdd.zero(e -> System.out.print(e));
            } catch (InterruptedException e) {
            }
        }).start();
        new Thread(()-> {
            try {
                zeroEvenOdd.even(e -> System.out.print(e));
            } catch (InterruptedException e) {
            }
        }).start();
    }
}

四、手写阻塞队列

实现一个有界阻塞队列,拥有put方法与take方法,当队列满时put方法阻塞,当队列空时take方法阻塞。

public class MyBlockingQueue {

    private Deque<Integer> queue;
    private int capacity;

    private Lock lock = new ReentrantLock();
    private Condition isFull = lock.newCondition();
    private Condition isEmpty = lock.newCondition();

    public MyBlockingQueue(int capacity) {
        this.queue = new LinkedList<>();
        this.capacity = capacity;
    }
    
    public void put(String item) throws InterruptedException {
        lock.lock();
        try {
            // 队列已满
            while (queue.size() >= capacity) {
                // put线程等待
                isFull.await();
            }
            queue.addLast(item);
            // 唤醒take线程
            isEmpty.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public String take() throws InterruptedException {
        lock.lock();
        String res = null;
        try {
            // 队列已满
            while (container.size() == 0) {
                // take线程等待
                isEmpty.await();
            }
            res = queue.pollFirst();
            // 唤醒put线程
            isFull.signalAll();
            return res;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        MyBlockingQueue blockingQueue = new MyBlockingQueue(64);
        new Thread(()-> {
            for (int i = 0; i < 100; i++) {
                try {
                    blockingQueue.put(String.valueOf(i));
                    System.out.println("放入" + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }).start();

        new Thread(()-> {
            for (;;) {
                try {
                    System.out.println("取出" + blockingQueue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }).start();
    }
}

五、按顺序输出字符串

问题描述:有4个线程和1个公共的字符串数组。线程1的功能就是向数组输出A,线程2的功能就是向数组输出B,线程3的功能就是向数组输出C,线程4的功能就是向数组输出D。要求按顺序向数组赋值ABCDABCDABCD,ABCD的个数由输入的参数指定。请编写一个类,类中封装公共字符串数组及输出逻辑,4个不同的线程将会共用一个该类的实例。

方法1:ReentrantLock + Condition

Condition强大的地方在于它能够精确的控制多线程的休眠与唤醒(注意是唤醒,唤醒只意味着进入了同步队列,不意味着一定能获得资源),相比使用Object的wait()/notify(),使用Condition的await()/signal()这种方式能够更加安全和高效地实现线程间协作

public class AbcdLock {
    private List<String> strList;
    private int n;

    private Lock lock = new ReentrantLock();
    private Condition aCondition = lock.newCondition();
    private Condition bCondition = lock.newCondition();
    private Condition cCondition = lock.newCondition();
    private Condition dCondition = lock.newCondition();

    public AbcdLock(List<String> strList, int n) {
        this.strList = strList;
        this.n = n;
    }

    public void addA() {
        for (int i = 0; i < n; ) {
            lock.lock();
            try {
                if (strList.size() % 4 != 0) {
                    // 添加A的线程等待
                    aCondition.await();
                }
                strList.add("A");
                i++;
                // 指定唤醒添加B的线程
                bCondition.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    public void addB() {
        for (int i = 0; i < n; ) {
            lock.lock();
            try {
                if (strList.size() % 4 != 1) {
                    // 添加B的线程等待
                    bCondition.await();
                }
                strList.add("B");
                i++;
                // 指定唤醒添加C的线程
                cCondition.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    public void addC() {
        for (int i = 0; i < n; ) {
            lock.lock();
            try {
                if (strList.size() % 4 != 2) {
                    // 添加C的线程等待
                    cCondition.await();
                }
                strList.add("C");
                i++;
                // 指定唤醒添加D的线程
                dCondition.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    public void addD() {
        for (int i = 0; i < n; ) {
            lock.lock();
            try {
                if (strList.size() % 4 != 3) {
                    // 添加A的线程等待
                    dCondition.await();
                }
                strList.add("D");
                i++;
                // 指定唤醒添加A的线程
                aCondition.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        List<String> strList = new ArrayList<>(128);
        AbcdLock abcdLock = new AbcdLock(strList, 4);
        new Thread(() -> {
            abcdLock.addA();
        }).start();
        new Thread(() -> {
            abcdLock.addB();
        }).start();
        new Thread(() -> {
            abcdLock.addC();
        }).start();
        new Thread(() -> {
            abcdLock.addD();
        }).start();
    }

方法2:synchronized关键字

只用一个Object锁,4个线程同时竞争一个锁,然后同时唤醒再竞争,再附加一个条件变量就行了。

将要输入的字符串以及字符串的丢失顺序,作为变量传入。只有当数组当前索引与本线程要输入字符串的位置匹配时,才做输入动作,否则线程等待。

public class AbcdSync extends Thread {

    private List<String> strList;
    private String       element;
    private int          cycle;
    // 用一个变量来规定要输入字符串的顺序,例如0、1、2、3
    private int          index;

    public AbcdSync(List<String> strList, String element, int cycle, int index) {
        this.strList = strList;
        this.element = element;
        this.cycle = cycle;
        this.index = index;
    }

    @Override
    public void run()  {
        try {
            for (int i = 0;i < cycle;) {
                synchronized (strList) {
                    // 这里的判断是关键
                    if (strList.size() % 4 == index) {
                        strList.add(element);
                        i++;
                        // 唤醒其他所有线程
                        strList.notifyAll();
                    } else {
                        // 当前线程阻塞
                        strList.wait();
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        List<String> strList = new ArrayList<>(256);
        new AbcdSync(strList, "A", 4, 0).start();
        new AbcdSync(strList, "B", 4, 1).start();
        new AbcdSync(strList, "C", 4, 2).start();
        new AbcdSync(strList, "D", 4, 3).start();
    }
}

六、模拟实现拍卖系统

问题描述:有3个人(线程)针对同一个商品展开竞价,商品起拍价格为100元,每个元都有自己的预算,出价不能超过预算,3个人轮流争抢式出价,后出的价格统一比前出的价格大100元,模拟实现这个过程。

分析:显然,谁的预算最大,谁将最终竞得商品,预算少的人依次退出竞价,最后仅剩一人

public class AuctionTest {
    // 存储a、b、c三人的预算
    private int aBudget;
    private int bBudget;
    private int cBudget;
    // 实时出价价格
    private int temp;

    private Lock      lock       = new ReentrantLock();
    private Condition aCondition = lock.newCondition();
    private Condition bCondition = lock.newCondition();
    private Condition cCondition = lock.newCondition();

    public AuctionTest(int a, int b, int c, int temp) {
        this.aBudget = a;
        this.bBudget = b;
        this.cBudget = c;
        this.temp = temp;
    }

    public void aQuote() throws InterruptedException {
        while (temp < aBudget) {
            lock.lock();
            try {
                if (temp + 100 > aBudget) {
                    aCondition.await();
                }
                temp = temp + 100;
                System.out.println("a出价:" + temp);
                bCondition.signal();
                cCondition.signal();
            } finally {
                System.out.println("a出价完成");
                lock.unlock();
            }
        }
    }

    public void bQuote() throws InterruptedException {
        while (temp < bBudget) {
            lock.lock();
            try {
                if (temp + 100 > bBudget) {
                    bCondition.await();
                }
                temp = temp + 100;
                System.out.println("b出价:" + temp);
                cCondition.signal();
                aCondition.signal();
            } finally {
                System.out.println("b出价完成");
                lock.unlock();
            }
        }
    }

    public void cQuote() throws InterruptedException {
        while (temp < cBudget) {
            lock.lock();
            try {
                if (temp + 100 > cBudget) {
                    cCondition.await();
                }
                temp = temp + 100;
                System.out.println("c出价:" + temp);
                aCondition.signal();
                bCondition.signal();
            } finally {
                System.out.println("c出价完成");
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        System.out.println("hello world");

        AuctionTest auctionTest = new AuctionTest(2000, 2400, 2500, 1000);

            new Thread(() -> {
                try {
                    auctionTest.aQuote();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();

            new Thread(() -> {
                try {
                    auctionTest.bQuote();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();

            new Thread(() -> {
                try {
                    auctionTest.cQuote();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
    }
}

模拟拍卖竞价的运行结果

hello world
a出价:1100
a出价完成
a出价:1200
a出价完成
a出价:1300
a出价完成
a出价:1400
a出价完成
a出价:1500
a出价完成
a出价:1600
a出价完成
a出价:1700
a出价完成
a出价:1800
a出价完成
a出价:1900
a出价完成
a出价:2000
a出价完成
b出价:2100
b出价完成
b出价:2200
b出价完成
b出价:2300
b出价完成
b出价:2400
b出价完成
c出价:2500
c出价完成

Process finished with exit code 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值