美文网首页
1114. 按序打印/Print in Order

1114. 按序打印/Print in Order

作者: GritMan | 来源:发表于2019-08-03 13:26 被阅读0次

    第一次接触多线程的编程题,之前仅仅是在准备java面试的时候看过几篇多线程相关的博客,在实际工作中由于没有使用场景,所以日渐生疏。趁此机会熟悉熟悉多线程。

    我的思路:volatile

    题目大概意思就是保证三个线程执行任务的有序性。直觉告诉我使用标志位控制有序。由于是多线程环境,我考虑使用volatile关键词修饰标志位,以达到保证在多线程环境中标志位的可见性。(其实只是我对多线程只知道一个volatile,不管三七二十一先用上试试。。。)

    class Foo {
    
    // 保证printSecond在printFirst之后执行的标志位
        volatile boolean isFirstPrinted = false;
    // 保证printThird在printSecond之后执行的标志位    
        volatile boolean isSecondPrinted = false;
        
        public Foo() {
            
        }
    
        public void first(Runnable printFirst) throws InterruptedException {
            
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            isFirstPrinted = true;
        }
    
        public void second(Runnable printSecond) throws InterruptedException {
            while(!isFirstPrinted){
                
            }
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
        }
    
        public void third(Runnable printThird) throws InterruptedException {
            while(!isFirstPrinted && !isSecondPrinted) {
                
            }
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
        }
    }
    

    居然过了。。。不愧是Easy,真适合我:)

    反思

    我的这种实现每个线程均处于不断的运行状态,相比于使用线程阻塞的实现来说存在很大的资源浪费。

    在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。

    扩展思路

    synchronized

    最基础的线程同步解决方案。

    class Foo {
    
        boolean isFirstPrinted = false;
        
        boolean isSecondPrinted = false;
        
        Object lock = new Object();
        
        public Foo() {
            
        }
    
        public void first(Runnable printFirst) throws InterruptedException {
            synchronized(lock) {
                // printFirst.run() outputs "first". Do not change or remove this line.
                printFirst.run();
                isFirstPrinted = true;
                lock.notifyAll();
            }
        }
    
        public void second(Runnable printSecond) throws InterruptedException {
            synchronized(lock) {
                while(!isFirstPrinted) {
                    lock.wait();
                }
                // printSecond.run() outputs "second". Do not change or remove this line.
                printSecond.run();
                isSecondPrinted = true;
                lock.notifyAll();
            }
        }
    
        public void third(Runnable printThird) throws InterruptedException {
            synchronized(lock) {
                while(!isSecondPrinted) {
                    lock.wait();
                }
                // printThird.run() outputs "third". Do not change or remove this line.
                printThird.run();
            }
        }
    }
    

    CountDownLatch

    CountDownLatch是一个计数器闭锁,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。
    CountDownLatch用一个给定的计数器来初始化,该计数器的操作是原子操作,即同时只能有一个线程去操作该计数器。调用该类await方法的线程会一直处于阻塞状态,直到其他线程调用countDown方法使当前计数器的值变为零,每次调用countDown,计数器的值减1。当计数器值减至零时,所有因调用await方法而处于等待状态的线程就会继续往下执行。

    import java.util.concurrent.CountDownLatch;
    
    class Foo {
    
        final CountDownLatch firstLatch = new CountDownLatch(1);
        
        final CountDownLatch secondLatch = new CountDownLatch(1);
        
        public Foo() {
            
        }
    
        public void first(Runnable printFirst) throws InterruptedException {
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            firstLatch.countDown();
        }
    
        public void second(Runnable printSecond) throws InterruptedException {
            firstLatch.await();
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            secondLatch.countDown();
        }
    
        public void third(Runnable printThird) throws InterruptedException {
            secondLatch.await();
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
        }
    }
    

    Semaphore

    SemaphoreCountDownLatch相似,不同的地方在于Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到底。它也被更多地用来限制流量,类似阀门的功能。如果限定某些资源最多有N个线程可以访问,那么超过N个主不允许再有线程来访问,同时当现有线程结束后,就会释放,然后允许新的线程进来。有点类似于锁的lockunlock过程。
    相对来说它也有两个主要的方法:用于获取权限的acquire,其底层实现与CountDownLatch.countdown类似;用于释放权限的release,其底层实现与acquire是一个互逆的过程。

    import java.util.concurrent.Semaphore;
    
    class Foo {
    
        final Semaphore firstSem = new Semaphore(0);
        
        final Semaphore secondSem = new Semaphore(0);
        
        public Foo() {
            
        }
    
        public void first(Runnable printFirst) throws InterruptedException {
            // printFirst.run() outputs "first". Do not change or remove this line.
            printFirst.run();
            firstSem.release();
        }
    
        public void second(Runnable printSecond) throws InterruptedException {
            firstSem.acquire();
            // printSecond.run() outputs "second". Do not change or remove this line.
            printSecond.run();
            secondSem.release();
        }
    
        public void third(Runnable printThird) throws InterruptedException {
            secondSem.acquire();
            // printThird.run() outputs "third". Do not change or remove this line.
            printThird.run();
        }
    }
    

    参考资料

    Java中的Volatile关键字详解

    Java并发之CountDownLatch、Semaphore和CyclicBarrier

    相关文章

      网友评论

          本文标题:1114. 按序打印/Print in Order

          本文链接:https://www.haomeiwen.com/subject/dcdjdctx.html