Java多线程的交替输出问题

前言:

面试题关于线程同步的面试题,凡是从时间角度或者是优先级角度考虑解决思路的,基本全不对!凡是从join sleep考虑的,99.99%的不对,线程优雅的结束,一般不用interrupt stop resume。


经典面试题:synchronized和ReentrantLock的区别是什么?

答案:ReentrantLock可以多个队列,synchronized只有一个队列,ReentrantLock可以做公平锁,synchronized只有非公平(没有公平可言),ReentrantLock还可以tryLock尝试上锁,上不了锁可以去做别的(做出一定的处理),但是synchronized只能上来就死等傻傻等待,等不到就死在那,第四个区别:锁可以被打断,就是我在锁的过程中我可以让别人打断我,打折我的腿我就醒了这个叫lock.incorruptibly可以做这个操作就是entry.lock,但是synchronized不可以,因为synchronized不能打断,除非把整个线程给砍掉。

提示:以下是本篇文章正文内容,下面案例可供参考

示例

用两个线程,一个输出字母,一个输出数字,交替输出

1A2B3C4D5E...26Z

  案例:wait();notify(); 写法 ,不推荐使用!(示例):

package com.aaa.blbl;

public class T06_sync_wait_notify {
    public static void main(String[] args) {
        //把o当中一把锁来使用,让数字和字母交替执行
        final Object o = new Object();

        //构建两个数组用来输出
        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        new Thread(() -> {
           synchronized (o)
           {
               for (char c : aI)
               {
                   System.out.println(c);
                   try {
                       o.notify();
                       o.wait();//让出锁
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
               o.notify();//必须,否则无法终止程序
           }
        },"t1").start();


        new Thread(() -> {
            synchronized (o)
            {
                for (char c : aC)
                {
                    try {
                        System.out.println(c);
                        o.notify();
                        o.wait();//让出锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                o.notify();//必须,否则无法终止程序
            }
        },"t2").start();
    }
}

解析:

O=锁,wait():进入这把锁锁所关联的那个等待队列。

举例:A上厕所,厕所里面有人怎么办,A到旁边一个有队列的地方去排队,

排排坐吃果果等什么时候轮到A了,A再去上厕所。

所以此锁附带的有一个等待队列,但是这个等待队列是所有的关于这把锁的线程都可以

让自己进去,不管他是进来上厕所还是洗手或者是接热水等等等等其它的各种骚操作,都可以进去,那先叫醒上洗手的或者是接热水的上厕所的不叫醒能不能做到。

答案:notify()做不到,notify的意思是说让线程调度器去选一个线程出来,他选谁这事你管不了,也就是说如果t1和t2两个线程全进去了t3想叫醒其中的某一个是叫不醒的,只能去按照特定算法去叫醒其中的一个,但是叫醒哪一个程序员无法控制,当然出了notify之外还有另外一种叫notifyall,notifyall原理:叫醒等待队列里面的所有线程,叫醒之后去抢那把锁,谁抢到算谁的,但是还是没有达到需求,因为会叫醒那些不想叫醒的线程。

某种条件下某种叫醒某些人他就是个队列。

举例2:我想叫醒某某些叫醒某些特定人群,该怎么办,我没办法,做不到。

举例3:著名的生产者消费者问题,生产者存放东西,消费者取东西 。

然后:必须对锁同步因为生产满了之后,生产者需要去进行休息等待进入队列。

消费者:消费者消费空了都得进入队列等待,那生产者生产满了,我想叫醒特定的那个消费者线程,里边那些生产者不想叫醒其余的消费者怎么办?

经典案例Condition:推荐使用

代码如下(示例):

package com.aaa.blbl;

import java.util.Collection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class T09_lock_condition {
    public static void main(String[] args) {
        //构建两个数组用来输出
        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        Lock lock = new ReentrantLock();
        //一个Condition代表一个队列,有几个Condition代表有几个队列
        Condition collectionT1 = lock.newCondition();//队列1
        Condition collectionT2 = lock.newCondition();//队列2

        CountDownLatch latch = new CountDownLatch(1);

        //进入线程:可选:让哪个队列进入当前线程
        new Thread(() -> {
            try {
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            lock.lock();
            try {
                for (char c : aI) {
                    System.out.println(c);
                    collectionT2.signal();
                    collectionT1.await();
                }

                collectionT2.signal();

            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        },"t1").start();

        new Thread(() ->{
           lock.lock();
           try {
               for (char c : aC)
               {
                   System.out.println(c);
                   latch.countDown();
                   collectionT1.signal();


                   collectionT2.await();
               }
           } catch (InterruptedException e) {
               e.printStackTrace();
           }finally {
               lock.unlock();
           }
        },"t2").start();
    }
}

好玩案例:酌情使用,使用同步队列实现

代码如下(示例):

package com.aaa.blbl;

import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
/*
TransferQueue最大好处容量为0,TransferQueue相当于一个交换场所
 */
public class T11_TransFerQueue {
    public static void main(String[] args) {
        char[] aI = "1234567".toCharArray();
        char[] aC = "ABCDEFG".toCharArray();

        TransferQueue<Character> queue = new LinkedTransferQueue<>();
        new Thread(() ->{

            try {
                for (char c : aI)
                {
                    //queue.take()从里边拿一个字符出来就把该字符打印出来
                    System.out.println(queue.take());
                    //queue.transfer(c) 往里递字符进去
                    queue.transfer(c);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t1").start();

        new Thread(() ->{
           try {
               for (char c : aC)
               {
                   //线程二是先递一个字符,再去拿一个打印出来。
                   //注意:transfer、take都是阻塞的,就是说如果take不到程序不会往下执行
                   //如果transfer过去了但是没有人从我手里拿走,那么我的程序也不会往下执行,类似于交换场所。
                   queue.transfer(c);
                   System.out.println(queue.take());
               }
           }catch (Exception e)
           {
               e.printStackTrace();
           }
        },"t2").start();
    }
}

总结

例如:以上就是今天要讲的内容,本文仅仅简单介绍了两种方式来实现效果,而如果只是利用线程实现交替输出效果还是有很多方式的!当然,笔者深度还欠缺,如果错误还请指正。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
### 回答1: Java多线程交替打印可以通过使用两个线程和一个共享变量来实现。假设我们需要交替打印数字1到100,其中一个线程负责打印奇数,另一个线程负责打印偶数。 首先,我们需要创建一个共享变量count,并初始化为1。然后,我们创建两个线程,分别为奇数线程和偶数线程。 奇数线程的任务是判断count的值是否小于等于100,如果是则打印count的值,并将count的值加1。然后,让偶数线程继续执行。 偶数线程的任务也是判断count的值是否小于等于100,如果是则打印count的值,并将count的值加1。然后,让奇数线程继续执行。 整个过程会一直进行下去,直到count的值大于100为止。 下面是具体实现的Java代码: ```java public class AlternatePrinting { private static volatile int count = 1; private static Object lock = new Object(); public static void main(String[] args) { Thread oddThread = new Thread(new PrintOdd()); Thread evenThread = new Thread(new PrintEven()); oddThread.start(); evenThread.start(); } static class PrintOdd implements Runnable { @Override public void run() { while (count <= 100) { synchronized (lock) { if (count % 2 != 0) { System.out.println(Thread.currentThread().getName() + ": " + count); count++; lock.notifyAll(); } else { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } static class PrintEven implements Runnable { @Override public void run() { while (count <= 100) { synchronized (lock) { if (count % 2 == 0) { System.out.println(Thread.currentThread().getName() + ": " + count); count++; lock.notifyAll(); } else { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } } ``` 上述代码中,使用了一个共享的锁对象lock来保证奇数线程和偶数线程之间的互斥和同步。同时,通过wait()和notifyAll()方法实现了线程的阻塞和唤醒操作。 通过以上代码,可以实现奇数和偶数的交替打印,输出结果类似如下: ``` Thread-0: 1 Thread-1: 2 Thread-0: 3 Thread-1: 4 ... Thread-0: 99 Thread-1: 100 ``` 这样就实现了Java多线程交替打印。 ### 回答2: Java多线程交替打印可以通过使用synchronized关键字和wait()、notify()、notifyAll()方法来实现。 首先,可以创建一个共享的对象作为锁,例如可以使用一个Object对象。 然后,创建两个线程,一个线程负责打印奇数,另一个线程负责打印偶数。在这两个线程的run()方法中,使用synchronized关键字来锁住共享对象。通过使用while循环来判断当前的数字是否满足打印条件,如果不满足则调用wait()方法等待;如果满足则打印数字并使用notify()方法唤醒其他线程。 在主线程中,使用start()方法启动这两个线程,并使用Thread.sleep()方法来控制每次打印的间隔时间。 具体实现如下: ``` public class AlternatePrinting { private static final Object lock = new Object(); private static int number = 1; public static void main(String[] args) { Thread oddThread = new Thread(() -> { while (number <= 10) { synchronized (lock) { if (number % 2 == 1) { System.out.println("奇数线程:" + number++); lock.notify(); } else { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }); Thread evenThread = new Thread(() -> { while (number <= 10) { synchronized (lock) { if (number % 2 == 0) { System.out.println("偶数线程:" + number++); lock.notify(); } else { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }); oddThread.start(); evenThread.start(); } } ``` 上述代码会创建两个线程,一个线程负责打印奇数,另一个线程负责打印偶数。在主线程中启动这两个线程,程序会交替打印奇数和偶数直到数字达到10为止。 ### 回答3: Java中,实现多线程交替打印可以使用两种方法:一是使用synchronized关键字,二是使用Lock和Condition接口。 方法一:使用synchronized关键字 首先,定义一个变量flag作为线程间的通信标志。然后,创建两个线程,分别使用synchronized锁住共享资源并通过wait()和notify()方法交替进行打印。具体步骤如下: 1. 创建一个实现Runnable接口的类,重写run()方法。 2. 在run()方法中,使用synchronized锁住共享资源。 3. 使用while循环,判断flag的值。若flag为true,则调用wait()方法等待,否则进行打印。 4. 打印完后,将flag的值取反,并使用notify()方法唤醒其他线程。 5. 在main()方法中,创建两个线程对象,分别调用start()方法启动线程。 方法二:使用Lock和Condition接口 这种方式使用Lock接口来锁住共享资源,并使用Condition接口的await()和signal()方法来实现线程间的交替打印。具体步骤如下: 1. 创建一个实现Runnable接口的类,重写run()方法。 2. 在run()方法中,使用Lock接口的lock()方法锁住共享资源。 3. 使用while循环,判断flag的值。若flag为true,则调用Condition接口的await()方法等待,否则进行打印。 4. 打印完后,将flag的值取反,并使用Condition接口的signal()方法唤醒其他线程。 5. 在main()方法中,创建一个ReentrantLock对象和两个Condition对象,分别调用lock()方法和newCondition()方法初始化。 6. 创建两个线程对象,分别调用start()方法启动线程。 这两种方法都可以实现多线程交替打印,具体选择哪种方式取决于具体的需求和场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

id_lian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值