线程交替打印奇偶数(信号量Semaphore实现)

实现方法一,信号量
思路:
两个线程都持有相同的一个信号量,奇数线程启动,获取信号量,然后打印完,释放信号量,再休眠,然后偶数线程启动,获取信号量,然后打印完,释放信号量

package demo6;

import java.util.concurrent.Semaphore;

/**
 * @ClassName AlternatePrinting
 * @Author laixiaoxing
 * @Date 2019/3/17 下午5:43
 * @Description 交替打印奇偶数
 * @Version 1.0
 */
public class AlternatePrinting {


    public static void main(String[] args) {

        Semaphore semaphore = new Semaphore(1);

        SemaphorePrintEven semaphorePrintEven = new SemaphorePrintEven(semaphore);
        Thread t2 = new Thread(semaphorePrintEven);
        t2.start();

        SemaphorePrintOdd semaphorePrintOdd = new SemaphorePrintOdd(semaphore);
        Thread t1 = new Thread(semaphorePrintOdd);
        t1.start();


    }

    /**
     * 使用信号量实现
     */
    static class SemaphorePrintOdd implements Runnable {

        int i = 0;
        private Semaphore semaphore;

        public SemaphorePrintOdd(Semaphore semaphore) {
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                while (true) {
                    i++;
                    if (i % 2 == 0) {
                        System.out.println("偶数线程:"+i);
                        semaphore.release();
                        Thread.sleep(2000);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class SemaphorePrintEven implements Runnable {

        int i = 0;
        private Semaphore semaphore;

        public SemaphorePrintEven(Semaphore semaphore) {
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                while (true) {
                    i++;
                    if (i % 2 == 1) {
                        System.out.println("奇数线程:"+i);
                        semaphore.release();
                        Thread.sleep(2000);
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


}


结果线程交替打印奇偶数(信号量Semaphore实现)_第1张图片

看起来没啥问题,但是!!! 那个休眠特别重要,因为如果不设置休眠时间,或者休眠设置的不合理,就不会是交替打印了。
因为,有这种情况,奇数线程打印完释放信号量,它如果有时间片执行 就会尝试再次去获取信号量,如果获取成功,那就又是奇数线程执行。

比如说去掉thread.sleep之后

public class AlternatePrinting {


    public static void main(String[] args) {

        Semaphore semaphore = new Semaphore(1);

        SemaphorePrintEven semaphorePrintEven = new SemaphorePrintEven(semaphore);
        Thread t2 = new Thread(semaphorePrintEven);
        t2.start();

        SemaphorePrintOdd semaphorePrintOdd = new SemaphorePrintOdd(semaphore);
        Thread t1 = new Thread(semaphorePrintOdd);
        t1.start();


    }

    /**
     * 使用信号量实现
     */
    static class SemaphorePrintOdd implements Runnable {

        int i = 0;
        private Semaphore semaphore;

        public SemaphorePrintOdd(Semaphore semaphore) {
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                while (true) {
                    i++;
                    if (i % 2 == 0) {
                        System.out.println("偶数线程:"+i);
                        semaphore.release();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class SemaphorePrintEven implements Runnable {

        int i = 0;
        private Semaphore semaphore;

        public SemaphorePrintEven(Semaphore semaphore) {
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                while (true) {
                    i++;
                    if (i % 2 == 1) {
                        System.out.println("奇数线程:"+i);
                        semaphore.release();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


}


线程交替打印奇偶数(信号量Semaphore实现)_第2张图片

会发现持续都是偶数或者奇数的情况!

那么怎么优化? 这里暂时只考虑信号量的实现方式。
那么就只能让奇数线程持有偶数线程的信号量,当奇数线程执行完之后,释放,偶数线程就可以执行,然后偶数线程持有奇数线程的信号量,执行完释放。

具体实现:
定义两个信号量,一个奇数信号量,一个偶数信号量,都初始化为1
先用掉偶数的信号量,因为要让奇数先启动,等奇数打印完再释放

奇数线程获取奇数信号量,执行打印,释放偶数线程的信号量,再次请求获取奇数信号量(因为之前已经获取过,因此会阻塞,除非收到偶数线程的释放)
偶数线程获取偶数信号量,执行打印,释放奇数信号量, 再次请求获取偶数信号量

package demo6;

import java.util.concurrent.Semaphore;

/**
 * @ClassName AlternatePrinting
 * @Author laixiaoxing
 * @Date 2019/3/17 下午5:43
 * @Description 交替打印奇偶数
 * @Version 1.0
 */
public class AlternatePrinting {


    public static void main(String[] args) throws InterruptedException {

        Semaphore semaphoreOld = new Semaphore(1);
        Semaphore semaphoreEven = new Semaphore(1);

        semaphoreOld.acquire();//让奇数先启动,所以先减掉偶数的信号量 等奇数线程来释放

        SemaphorePrintEven semaphorePrintEven = new SemaphorePrintEven(semaphoreOld, semaphoreEven);
        Thread t1 = new Thread(semaphorePrintEven);
        t1.start();

        SemaphorePrintOdd semaphorePrintOdd = new SemaphorePrintOdd(semaphoreOld, semaphoreEven);
        Thread t2 = new Thread(semaphorePrintOdd);
        t2.start();

    }

    /**
     * 使用信号量实现
     */
    static class SemaphorePrintOdd implements Runnable {

        int i = 0;
        private Semaphore semaphoreOdd;
        private Semaphore semaphoreEven;


        public SemaphorePrintOdd(Semaphore semaphoreOdd, Semaphore semaphoreEven) {
            this.semaphoreOdd = semaphoreOdd;
            this.semaphoreEven = semaphoreEven;
        }

        @Override
        public void run() {
            try {
                semaphoreOdd.acquire();//获取信号量
                while (true) {
                    i++;
                    if (i % 2 == 0) {
                        System.out.println("偶数线程:" + i);
                        semaphoreEven.release();
                        //再次申请获取偶数信号量,因为之前已经获取过,如果没有奇数线程去释放,那么就会一直阻塞在这,等待奇数线程释放
                        semaphoreOdd.acquire();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class SemaphorePrintEven implements Runnable {

        int i = 0;
        private Semaphore semaphoreOdd;
        private Semaphore semaphoreEven;


        public SemaphorePrintEven(Semaphore semaphoreOdd, Semaphore semaphoreEven) {
            this.semaphoreOdd = semaphoreOdd;
            this.semaphoreEven = semaphoreEven;
        }

        @Override
        public void run() {

            try {
                semaphoreEven.acquire();
                while (true) {
                    i++;
                    if (i % 2 == 1) {
                        System.out.println("奇数线程:" + i);
                        semaphoreOdd.release();
                        semaphoreEven.acquire();//再次申请获取奇数信号量,需要等偶数线程执行完然后释放该信号量,不然阻塞
                    }
                }

            } catch (Exception ex) {}


        }
    }
}

线程交替打印奇偶数(信号量Semaphore实现)_第3张图片

需要注意的是,如果某个线程来不及释放就异常中断了,会导致另一个线程一直在等,造成死锁。 虽然这个异常不在这个问题的考虑范围内

你可能感兴趣的:(java,多线程并发,并发编程)