Java 用两个线程交替打印数字和字母

前一段时间听马士兵老师讲课,讲到某公司的一个面试,两个线程,其中一个线程输出ABC,另一个线程输出123,如何控制两个线程交叉输出1A2B3C,由于本人多线程掌握的一直不是很好,所以听完这道题,个人感觉收获良多,这是一个学习笔记。这道题有多种解法,不过有些属于纯炫技,所以只记录常见的三种解法。首先看第一种

1. park 和 unpark

package cn.bridgeli.demo;
 
import com.google.common.collect.Lists;
 
import java.util.List;
import java.util.concurrent.locks.LockSupport;
 
/**
 * @author BridgeLi
 * @date 2021/2/6 16:14
 */
public class Thread_Communication_Park_Unpark {
 
    static Thread t1 = null;
    static Thread t2 = null;
 
    public static void main(String[] args) {
 
        final List<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7);
        final List<String> strings = Lists.newArrayList("A", "B", "C", "D", "E", "F", "G");
 
        t1 = new Thread(() -> integers.forEach(item -> {
            System.out.print(item);
            LockSupport.unpark(t2);
            LockSupport.park();
        }), "t1");
 
        t2 = new Thread(() -> strings.forEach(item -> {
            LockSupport.park();
            System.out.print(item);
            LockSupport.unpark(t1);
        }), "t2");
 
        t1.start();
        t2.start();
    }
 
}

这个是最简单的实现方法,LockSupport.park() 使当前线程阻塞,而 LockSupport.unpark() 则表示唤醒一个线程,所以他需要一个参数,表示你要唤醒哪个线程,很好理解,也比较简单。

2. synchronized、notify、wait

package cn.bridgeli.demo;
 
import com.google.common.collect.Lists;
 
import java.util.List;
 
/**
 * @author BridgeLi
 * @date 2021/2/6 16:14
 */
public class Thread_Communication_Notify_Wait {
 
    public static void main(String[] args) {
 
        final Object o = new Object();
        final List<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7);
        final List<String> strings = Lists.newArrayList("A", "B", "C", "D", "E", "F", "G");
 
        new Thread(() -> {
            synchronized (o) {
                integers.forEach(item -> {
                    System.out.print(item);
                    o.notify();
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
 
                o.notify();
            }
        }, "t1").start();
 
        new Thread(() -> {
            synchronized (o) {
                strings.forEach(item -> {
                    System.out.print(item);
                    o.notify();
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
                o.notify();
            }
        }, "t2").start();
    }
}

这是一个比较传统的写法,也是比较难理解的一个写法,掌握了这种写法之后,对 synchronized、notify、wait 的认识也会有一个新高度,下面就简单解析一下这种写法:

我们都知道 synchronized 是一把锁,而锁是什么?就是一个第三方的互斥的一个资源,所以 synchronized (o),就表示我们对 o 这个对象加锁,是通过修改 o 的对象头实现的,也就是两个线程谁成功修改了 o 的对象头,那么谁就拿到了这把锁,然后就可以执行里面的相关逻辑,而没有成功修改 o 的对象头的线程,就只有进入到对象 o 的一个等待队列,等待被系统调度执行(这是一个比较简单的不是很准确说法,详细过程,等我将来再写一个文章想聊锁升级的过程);然后就是 o.notify(),刚说过 synchronized (o) 一堆线程争抢锁,没有抢到锁的线程进入对象 o 的等待队列,所以 o.notify() 含义就是从对象 o 的等待队列中随机叫醒一个线程,然后执行;最后是 o.wait() 的含义,他的含义也很简单,就是当前线程放到对象 o 的等待队列中,让出 CPU。

通过这段描述,所以大家肯定也可以学习到经常遇到的三个问题是怎么回事:1. wait 是否占用 CPU 资源,因为进入了等待队列,所以是不会占用的;2. 既然 notify、wait 是让唤醒线程和让线程进入等待的,为什么不是 Thread 类的方法,反而是 Object 的方法,因为 notify、wait 是配合 synchronized 一起使用的,不一定用在多线程中,他们控制的是 synchronized 锁定的对象的等待队列,而 synchronized 锁定的对象,肯定是一个 Object,所以 notify、wait 比如是 Object 对象的方法;3. 关于 synchronized (o) 括号里面是一个对象实例、Class 对象、锁定代码块、静态变量等等区别,只要明白 synchronized 修改的是什么,这些区别就一目了然了,不再赘述。

最后要说明的一个问题是:循环外边的 o.notify() 必不可少,有些同学写的时候可能随手就忘记了,或者不知道为什么需要最后再 notify 一下,其实仔细想一想就可以明白了,假设最后执行的是输出字母的线程,那么他之前一定是被执行输出数字的线程唤醒的,而执行输出数字的这个线程唤醒执行输出字母的线程之后,自身就进入等待队列了,所以循环结束之后,如果最后执行输出字母的线程没有唤醒执行输出数字的线程的话,那么执行输出数字的线程会一直 wait 阻塞在那里,将等到天荒地来海枯石烂永远无法结束。

3. Condition

package cn.bridgeli.demo;
 
import com.google.common.collect.Lists;
 
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * @author BridgeLi
 * @date 2021/2/6 16:14
 */
public class Thread_Communication_Condition {
 
    public static void main(String[] args) {
 
        final List<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7);
        final List<String> strings = Lists.newArrayList("A", "B", "C", "D", "E", "F", "G");
 
        Lock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
 
        new Thread(() -> {
            lock.lock();
            try {
                integers.forEach(item -> {
                    System.out.print(item);
                    condition2.signal();
                    try {
                        condition1.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
 
                condition2.signal();
            } finally {
                lock.unlock();
            }
        }, "t1").start();
 
        new Thread(() -> {
            lock.lock();
            try {
                strings.forEach(item -> {
                    System.out.print(item);
                    condition1.signal();
                    try {
                        condition2.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
                condition1.signal();
            } finally {
                lock.unlock();
            }
        }, "t2").start();
 
    }
 
}

当我们理解了上面两种写法之后,那么最后这个写法其实也比较容易理解了,就不用我多赘言了。

如果有幸有同学看到了这里,那么我再提出一个小问题,可以思考一下怎么解决,后面两种写法,我们保证是执行输出数字的线程还是执行输出字母的线程先执行,也就是先输出数字或者字母吗?如果不能的话,现在业务需求要求必须是先输出字母或者数字怎么做?(提示:CAS 自旋)

以上就是Java 用两个线程交替打印数字和字母的详细内容,更多关于Java 线程交替打印的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java线程使用同步锁交替执行打印奇数偶数的方法

    对同一个对象进行多线程操作时,如何保证线程执行结果的一致性?我们需要对线程操作对象加同步锁.(这是一道面试题) 需求描述 1-20个数字 A线程打印奇数:1,3,5,7,9,11,13,15,17,19 B线程打印偶数:2,4,6,8,10,12,14,16,18,20 C线程在AB两个线程执行完了之后打印结果:"success". 线程代码实现 Num.java package com.boonya.thread.test; /** * @ClassName: Num * @Desc

  • java实现多线程交替打印两个数

    本文实例为大家分享了java实现多线程交替打印两个数的具体代码,供大家参考,具体内容如下 方法1.使用wait和notify package com.thread; public class T01 { public static void main(String[] args) { char[] char1 = "AAAAAA".toCharArray(); char[] char2 = "BBBBBB".toCharArray(); Object object

  • java实现多线程交替打印

    本文实例为大家分享了java实现多线程交替打印的具体代码,供大家参考,具体内容如下 notify+wait实现 import org.junit.Test; import java.util.concurrent.*; public class TestThreadLocal { Object o = new Object(); CountDownLatch c=new CountDownLatch(2); @Test public void vvvvvvvv() throws Interrup

  • Java 实现多线程切换等待唤醒交替打印奇偶数

    引言 在日常工作生活中,可能会有用时几个人或是很多人干同一件事,在java编程中,同样也会出现类似的情况,多个线程干同样一个活儿,比如火车站买票系统不能多个人买一到的是同一张票,当某个窗口(线程)在卖某一张票的时候,别的窗口(线程)不允许再卖此张票了,在此过程中涉及到一个锁和资源等待的问题,如何合理正确的让线程与线程在干同一件事的过程中,不会抢资源以及一个一直等待一个一直干活的状况,接下来就聊一下多线程的等待唤醒以及切换的过程,在此就以A和B两个线程交替打印奇偶数的例子为例,代码如下: pack

  • Java多线程通信:交替打印ABAB实例

    使用wait()和notify()实现Java多线程通信:两个线程交替打印A和B,如ABABAB public class Test { public static void main(String[] args) { final PrintAB print = new PrintAB(); new Thread(new Runnable() { public void run(){ for(int i=0;i<5;i++) { print.printA(); } } }).start(); n

  • java实现两个线程交替打印的实例代码

    使用ReentrantLock实现两个线程交替打印 实现字母在前数字在后 package com.study.pattern; import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public c

  • Java 用两个线程交替打印数字和字母

    前一段时间听马士兵老师讲课,讲到某公司的一个面试,两个线程,其中一个线程输出ABC,另一个线程输出123,如何控制两个线程交叉输出1A2B3C,由于本人多线程掌握的一直不是很好,所以听完这道题,个人感觉收获良多,这是一个学习笔记.这道题有多种解法,不过有些属于纯炫技,所以只记录常见的三种解法.首先看第一种 1. park 和 unpark package cn.bridgeli.demo;   import com.google.common.collect.Lists;   import ja

  • C++详解如何实现两个线程交替打印

    C++线程库,点击此处查看文档 首先简单搭一个框架,让两个线程先尝试实现交替打印. //实现两个线程交替打印 #include <iostream> #include <thread> using namespace std; int main(void) { int n = 100; int i = 0; //创建两个线程 thread t1([&n, &i](){ while (i < n) { cout << i << "

  • Java中实现两个线程交替运行的方法

    今天笔者收到老师的一个题目,让我准备两个流程,依次实现输出以下信息 如: 线程A 打印 字母a ,线程B 打印数字1 线程A 打印 字母b ,线程B 打印数字2 线程A 打印 字母c ,线程B 打印数字3 线程A 打印 字母d ,线程B 打印数字4 ... 依次打印完毕26个字母和26个数字 ,输出效果为: a1b2c3...z26 下文笔者就将具体的实现思路展示如下: 1.将借助多线程的wait方法 2.借助一个外部变量 package com.java265.other; public cl

  • python 实现两个线程交替执行

    我就废话不多说,直接看代码吧! import threading import time def a(): while True: lockb.acquire() print('a') locka.release() time.sleep(0.5) def b(): while True: locka.acquire() print('b') lockb.release() time.sleep(0.5) if __name__ == "__main__": locka = threa

  • C++11用两个线程轮流打印整数的实现方法

    使用C++11标准的的线程语法,用两个线程轮流打印整数,一个线程打印奇数,一个线程打印偶数.可以练习线程的基本操作.线程锁和条件变量等技术.完整代码如下.代码后面附有主要语句的讲解. #include <thread> #include <iostream> #include <mutex> #include <condition_variable> std::mutex data_mutex; std::condition_variable data_va

  • go语言实现两个协程交替打印

    目录 方法一:使用两个channel 方法二 :使用一个channel 方法一:使用两个channel 这里channel CA 必须要有缓冲区,否则最后会报错 fatal error: all goroutines are asleep - deadlock! 这是因为无缓冲的通道只有在有接收方能够接收值的时候才能发送成功,否则会一直处于等待发送的阶段.因为最后交替运行完后没有协程可以接收CA通道中的数据,所以会一直阻塞发生死锁 package main import (     "fmt&q

  • Java 创建两个线程模拟对话并交替输出实现解析

    需求说明 实现方式 嗯 这个可以视作一个经典的消费者和生产者的问题,详细见代码注释 代码内容 消费者,负责取走生产者产生的信息 /** * @auther::9527 * @Description: 消费者 * @program: 多线程 * @create: 2019-08-10 10:45 */ public class Consumer extends Thread{ private MiddleMan mid = null; public Consumer() { } public Co

  • python使用threading.Condition交替打印两个字符

    Python中使用threading.Condition交替打印两个字符的程序. 这个程序涉及到两个线程的的协调问题,两个线程为了能够相互协调运行,必须持有一个共同的状态,通过这个状态来维护两个线程的执行,通过使用threading.Condition对象就能够完成两个线程之间的这种协调工作. threading.Condition默认情况下会通过持有一个ReentrantLock来协调线程之间的工作,所谓可重入锁,是只一个可以由一个线程递归获取的锁,此锁对象会维护当前锁的所有者(线程)和当前所

随机推荐