循环打印ABC/AB最简代码

1,382 阅读3分钟

信号量的方式

这是比较推荐的一种方式,不用控制线程启动的顺序. 思路简单,直接上代码,如果需要增加打印次数也很方便.


    static class PrintSemaphore implements Runnable {
        String printValue;

        Semaphore selfSemaphore;

        Semaphore nextSemaphore;

        public void setSelfSemaphore(Semaphore selfSemaphore) {
            this.selfSemaphore = selfSemaphore;
        }

        public void setNextSemaphore(Semaphore nextSemaphore) {
            this.nextSemaphore = nextSemaphore;
        }

        public PrintSemaphore(String printValue) {
            this.printValue = printValue;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    selfSemaphore.acquire();//获取信号量,s - 1
                    System.out.println(printValue);
                    nextSemaphore.release();//释放信号量,s + 1
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }


      public static void main(String[] args) {
        //信号量,用于控制同时容许访问的线程数量
        Semaphore sA = new Semaphore(1);
        Semaphore sB = new Semaphore(0);
        Semaphore sC = new Semaphore(0);

        PrintSemaphore printSemaphoreA = new PrintSemaphore("A");
        PrintSemaphore printSemaphoreB = new PrintSemaphore("B");
        PrintSemaphore printSemaphoreC = new PrintSemaphore("C");

        printSemaphoreA.setSelfSemaphore(sA);
        printSemaphoreA.setNextSemaphore(sB);

        printSemaphoreB.setSelfSemaphore(sB);
        printSemaphoreB.setNextSemaphore(sC);

        printSemaphoreC.setSelfSemaphore(sC);
        printSemaphoreC.setNextSemaphore(sA);


        ExecutorService exe = Executors.newCachedThreadPool();
        exe.execute(printSemaphoreB);
        exe.execute(printSemaphoreC);
        exe.execute(printSemaphoreA);


    }

使用synchronized

分析

看一个序列CABC,可以得出:
CA, 打印A需要拿到前面C的锁(代表C已经执行完),以及它自己的锁.
AB,打印B需要拿到前面A的锁(代表A已经执行完),以及它自己的锁.
BC,打印A需要拿到前面B的锁(代表B已经执行完),以及它自己的锁.
只要打印ABC的线程,按顺序启动,即B启动时A已经执行完一次,C启动时B已经执行完一次.那么序列的顺序就是可以保证的.

模拟

打印A线程先启动,拿到C+A锁,执行完后,释放A锁,并notifyAll.然后在C锁上等待.
在间隔时间段后,打印B线程启动,它请求A锁并拿到,然后 拿到自己的B锁,打印B.
,释放B锁,并notifyAll.然后在A锁上等待.
注意此时:打印A线程 wait C,打印B线程 wait A
在间隔时间段后,打印C线程启动,它请求B锁并拿到,然后 拿到自己的C锁,打印C.
,释放C锁,并notifyAll.然后在B锁上等待.
这时我们发现打印A线程的条件满足了,它可以得到C锁和自己的A锁.继续循环
注意此时:打印B线程 wait A,打印C线程 wait B
打印ABC

class PrintThreadSyn implements Runnable {
    // 打印值
    String value;
    // 前面的锁
    Object preLock;

    public void setPreLock(Runnable runnable) {
        preLock = runnable;
    }

    public PrintThreadSyn(String value) {
        this.value = value;
    }

    @Override
    public void run() {

        synchronized (preLock) {
            while (true) {
                synchronized (this) {
                    System.out.println(value);
                    this.notifyAll();
                }
                try {
                    preLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

public static void main(String[] args) throws InterruptedException {
        // ------------------------------synchronized--ABC-----------------------------
        //  循环打印ABC
        PrintThreadSyn threadA = new PrintThreadSyn("A");
        PrintThreadSyn threadB = new PrintThreadSyn("B");
        PrintThreadSyn threadC = new PrintThreadSyn("C");

        threadA.setPreLock(threadC);
        threadB.setPreLock(threadA);
        threadC.setPreLock(threadB);

        new Thread(threadA).start();
        Thread.sleep(500);
        new Thread(threadB).start();
        Thread.sleep(500);
        new Thread(threadC).start();
}

打印AB 只需要简化代码,对类无改动

        //  循环打印AB
        PrintThreadSyn threadA = new PrintThreadSyn("A");
        PrintThreadSyn threadB = new PrintThreadSyn("B");

       threadA.setPreLock(threadB);
       threadB.setPreLock(threadA);
			 
       new Thread(threadA).start();
			 Thread.sleep(500);
       new Thread(threadB).start();

ReentrantLock

思路是一样的,用ReentrantLock+Condition代替syn+notify+wait.

 class PrintThreadReentrantLock implements Runnable {
        String value;
        ReentrantLock lock;
        Condition preCondition;
        Condition selfCondition;

        public void setLock(ReentrantLock lock) {
            this.lock = lock;
        }

        public void setPreCondition(Condition condition) {
            this.preCondition = condition;
        }

        public void setSelfCondition(Condition condition) {
            this.selfCondition = condition;
        }

        public PrintThreadReentrantLock(String value) {
            this.value = value;
        }

        @Override
        public void run() {

            lock.lock();
            while (true) {

                System.out.println(value);
                selfCondition.signalAll();
                try {
                    preCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            //lock.unlock();
        }
    }
public static void main(String[] args) throws InterruptedException {

		    PrintThreadReentrantLock threadA = new PrintThreadReentrantLock("A");
        PrintThreadReentrantLock threadB = new PrintThreadReentrantLock("B");
        PrintThreadReentrantLock threadC = new PrintThreadReentrantLock("C");

        ReentrantLock reentrantLock = new ReentrantLock();
        threadA.setLock(reentrantLock);
        threadB.setLock(reentrantLock);
        threadC.setLock(reentrantLock);


        Condition conditionA = reentrantLock.newCondition();
        Condition conditionB = reentrantLock.newCondition();
        Condition conditionC = reentrantLock.newCondition();
        threadA.setSelfCondition(conditionA);
        threadB.setSelfCondition(conditionB);
        threadC.setSelfCondition(conditionC);

        threadA.setPreCondition(conditionC);
        threadB.setPreCondition(conditionA);
        threadC.setPreCondition(conditionB);

        new Thread(threadA).start();
        Thread.sleep(500);
        new Thread(threadB).start();
        Thread.sleep(500);
        new Thread(threadC).start();
	}			

打印AB 只需要简化代码,对类无改动

        PrintThreadReentrantLock threadA = new PrintThreadReentrantLock("A");
        PrintThreadReentrantLock threadB = new PrintThreadReentrantLock("B");

        ReentrantLock reentrantLock = new ReentrantLock();
        threadA.setLock(reentrantLock);
        threadB.setLock(reentrantLock);

        Condition conditionA = reentrantLock.newCondition();
        Condition conditionB = reentrantLock.newCondition();
        threadA.setSelfCondition(conditionA);
        threadB.setSelfCondition(conditionB);

        threadA.setPreCondition(conditionB);
        threadB.setPreCondition(conditionA);

        new Thread(threadA).start();
        Thread.sleep(500);
        new Thread(threadB).start();