美文网首页
数字字母顺序执行

数字字母顺序执行

作者: 一生逍遥一生 | 来源:发表于2020-04-27 19:35 被阅读0次

问题:用两个线程,一个输出字母,一个输出数字,交替输出。1A2B3C4D5E6F7G。

LockSupport

public class ParkAndUnPark {
    static Thread t1 = null,t2 = null;
    public static void main(String[] args) {
        char[] numbers = "1234567".toCharArray();
        char[] characters = "ABCDEFG".toCharArray();
        t1 = new Thread(()->{
            for (char ch:numbers){
                // 先输出一个数字
                System.out.println(ch);
                // 唤醒t2线程,t2执行
                LockSupport.unpark(t2);
                // 把当前线程挂起
                LockSupport.park();
            }
        },"t1");

        t2 = new Thread(()->{
            for (char ch:characters){
                // 将当前线程挂起
                LockSupport.park();
                // 输出字母
                System.out.println(ch);
                // 唤醒t1线程
                LockSupport.unpark(t1);

            }
        },"t2");
        t1.start();
        t2.start();
    }
}

LockSupport park/unpark底层实现为Unsafe park/unpark。

在Linux系统下,是用的Posix线程库pthread中的mutex(互斥量),condition(条件变量)来实现的。
mutex和condition保护了一个_counter的变量,当park时,这个变量被设置为0,当unpark时,这个变量被设置为1。

park 的过程

1.当调用park时,先尝试能否直接拿到“许可”,即_counter>0时,如果成功,则把_counter设置为0,并返回;
2.如果不成功,则构造一个ThreadBlockInVM,然后检查_counter是不是>0,如果是,则把_counter设置为0,unlock mutex并返回;
3.否则,再判断等待的时间,然后再调用pthread_cond_wait函数等待,如果等待返回,则把_counter设置为0,unlock mutex并返回;

unpark 的过程

当unpark时,则简单多了,直接设置_counter为1,再unlock mutex返回。如果_counter之前的值是0,则还要调用pthread_cond_signal唤醒在park中等待的线程;

Spin Lock

使用自旋的方式,不断的尝试,知道获取这个锁。在JVM中,可以开启自旋锁,默认值为10;
如果第一次可以获取到锁,第二次就可以循环10次以上,就可以获取到锁。

public class SpinLock {
    static Thread t1 = null, t2 = null;

    enum ReadyToRun {T1, T2}

    static ReadyToRun r = ReadyToRun.T1;

    public static void main(String[] args) {
        char[] numbers = "1234567".toCharArray();
        char[] characters = "ABCDEFG".toCharArray();
        t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c : numbers) {
                    // 消耗CPU,不断判断
                    while (r != ReadyToRun.T1) {
                    }
                    System.out.print(c);
                    r = ReadyToRun.T2;
                }

            }
        });
        t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c : characters) {
                    while (r != ReadyToRun.T2) {
                    }
                    System.out.print(c);
                    r = ReadyToRun.T1;
                }

            }
        });
        t1.start();
        t2.start();
    }
}

CAS

Atomicxxx底层实现为CAS。

public class CASOrder {
    static Thread t1 = null, t2 = null;
    static AtomicInteger a = new AtomicInteger(1);
    public static void main(String[] args) {
        char[] numbers = "1234567".toCharArray();
        char[] characters = "ABCDEFG".toCharArray();
        t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c : numbers) {
                    while (a.get() != 1) {
                    }
                    System.out.print(c);
                    a.set(2);
                }

            }
        });
        t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c : characters) {
                    while (a.get() != 2) {
                    }
                    System.out.print(c);
                    a.set(1);
                }
            }
        });
        t1.start();
        t2.start();
    }
}

BlockingQueue

使用两个不同的阻塞队列,可以实现顺序。如果是两个线程顺序执行的话(固定的内容),可以使用BlockingQueue或者ReentrantLock和Condition。
实现多生产者和消费者需要使用ReentrantLock和Condition,可以考虑使用两个Lock实现,可以借鉴LinkedBlockingQueue。
如果只是实现顺序执行的话,可以使用join()、CountDownLatch、CyclicBarrier。

public class BlockingLock {
    static Thread t1 = null, t2 = null;
    static BlockingQueue<String> bq1 = new ArrayBlockingQueue<>(1);
    static BlockingQueue<String> bq2 = new ArrayBlockingQueue<>(1);
    public static void main(String[] args) {
        char[] numbers = "1234567".toCharArray();
        char[] characters = "ABCDEFG".toCharArray();
        t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c : numbers) {
                    System.out.print(c);
                    try {
                        bq1.put("ok");
                        bq2.take();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c : characters) {
                    try {
                        bq1.take();
                        System.out.print(c);
                        bq2.put("ok");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }
        });
        t2.start();
        t1.start();
    }
}

CountDownLatch

public class CountDownLock {
    static Thread t1 = null, t2 = null;
    static CountDownLatch latch = new CountDownLatch(1);
    // 自旋占用CPU 速度特别快,执行时间特别短,线程特别多
    // 重量级锁 线程特别多,只有部分执行时间较长,需要向内核态申请资源
    public static void main(String[] args) {
        char[] numbers = "1234567".toCharArray();
        char[] characters = "ABCDEFG".toCharArray();
        final Object o = new Object();
        t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                // 不能使用this,必须要保证两个线程是使用的同一个锁
                synchronized (o) {
                    for (char c : numbers) {
                        System.out.print(c);
                        latch.countDown();
                        try {
                            o.notify(); // 获取其中一个线程
                            o.wait(); //进入waitset
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    }
                    o.notify(); //如果没有这一步就会造成不会退出
                }
            }
        });
        t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o) {
                    for (char c : characters) {
                        System.out.print(c);
                        try {
                            o.notify(); // 获取其中一个线程
                            o.wait(); //进入waitset
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    o.notify();  //如果没有这一步就会造成不会退出
                }
            }
        });
        t1.start();
        t2.start();
    }
}

Synchronized

public class SynchronizedLock {
    static Thread t1 = null, t2 = null;
    public static void main(String[] args) {
        char[] numbers = "1234567".toCharArray();
        char[] characters = "ABCDEFG".toCharArray();
        Lock lock = new ReentrantLock();
        //一把锁两种条件
        Condition conditionT1 = lock.newCondition();
        Condition conditionT2 = lock.newCondition();
        CountDownLatch latch = new CountDownLatch(1);
        t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    for (char c : numbers) {
                        System.out.print(c);
                        latch.countDown();
                        conditionT2.signal();
                        conditionT1.await();
                    }
                    conditionT2.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });
        t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    latch.await();
                    lock.lock();
                    for (char c : characters) {
                        System.out.print(c);
                        conditionT1.signal();
                        conditionT2.await();
                    }
                    conditionT1.signal();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });
        t1.start();
        t2.start();
    }
}

参考文献

LockSupport(park/unpark)源码分析
面试 LockSupport.park()会释放锁资源吗?
LockSupport park和unpark
两个线程,输出数字和字母
面试题。用两个线程

相关文章

  • 数字字母顺序执行

    问题:用两个线程,一个输出字母,一个输出数字,交替输出。1A2B3C4D5E6F7G。 LockSupport L...

  • python接口自动化-pytest-用例执行顺序

    一、默认执行顺序 pytest默认按字母顺序去执行的(小写英文—>大写英文—>0-9数字)用例之间的顺序是文件之间...

  • 测试用例的运行

    假如有多个测试用例,按照字母(ASCII编码)执行顺序我们可以在不同的字母前面加数字表示先后顺序更方便简洁eg:t...

  • -02-测试框架中用例的执行顺序

    测试框架中用例的执行顺序: 1、数字优先 2、大写字母其次 3、小写字母最后 以上排序,科学的参考依据:ASCII...

  • 过滤器-06-19

    Filter;实现接口 重写方法:init(),doFilter(),destroy() 执行顺序:注解按字母顺序...

  • 第二天_java基础语法(2018-07-26)

    1、main方法【函数】,主方法、程序的入口。代码是按照顺序执行的 2、标识符: 规则:1、有字母、数字、下划线、...

  • js 数组排序

    js 数组默认是按照数字的字母顺序排序

  • 密码故事——最最简单地攻防(1)——Zfund量化套利基金

    字母表顺序和数字 加密的时候,经常要把A~Z这26个字母转换成数字,最常见的一种方法就是取字母表中的数字序号...

  • PHP 基础学习

    定义函数跟JS类似,只是输出使用 echo函数名的首字母不可以使用数字return 会中断执行顺序 局部/全局变量...

  • String字符计算

    大小写转换 字母和数字在Unicode 表中是顺序排列的'0','1','2'......'9'大写字母和小写字母...

网友评论

      本文标题:数字字母顺序执行

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