Google多线程面试题: 4个线程向4个文件里写入数据, 每个线程只能写一个值

By Long Luo

个人博客链接

最近在学习Java多线程时,遇到了一个下面的笔试题,题目如下:

编写一个程序,程序会启动4个线程,向4个文件A,B,C,D里写入数据,每个线程只能写一个值。
    线程A:只写A
    线程B:只写B
    线程C:只写C
    线程D:只写D 

4个文件A,B,C,D。 

程序运行起来,4个文件的写入结果如下:
    A:ABCDABCD...
    B:BCDABCDA...
    C:CDABCDAB...
    D:DABCDABC...

网上搜索了下,好像还是一个Google笔试题,这个问题涉及到的知识点有:多线程, 并发, , 线程间通信

个人分析过程:
1. 创建4个线程;
2. 每个线程在输出时都需要加锁;
3. 操作文件的代码要加锁;
4. 一个线程完成之后,通知下一个要执行的线程;

根据以上分析,可以写出如下代码:

package com.imlongluo.Practise;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Practise {

    public static void main(String[] args) {

        final Task task = new Task();

        /** 创建4个线程,同时启动 */
        /**
         * Thread A
         */
        new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    task.outputA();
                }
            }
        }, " Thread A").start();

        /**
         * Thread B
         */
        new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    task.outputB();
                }
            }
        }, "Thread B").start();

        /**
         * Thread C
         */
        new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    task.outputC();
                }
            }
        }, "Thread C").start();

        /**
         * Thread D
         */
        new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    task.outputD();
                }
            }
        }, "Thread D").start();
    }

    /**
    任务类
    */
    public static class Task {
        /**
        创建一个Lock锁对象
        */
        private Lock lock = new ReentrantLock();

        private BufferedWriter bw1 = null;
        private BufferedWriter bw2 = null;
        private BufferedWriter bw3 = null;
        private BufferedWriter bw4 = null;

        private int ctl = 0;

        /**
         * Condition: 线程条件
         */
        private Condition cond1 = lock.newCondition();
        private Condition cond2 = lock.newCondition();
        private Condition cond3 = lock.newCondition();
        private Condition cond4 = lock.newCondition();

        private boolean[] outputThisRound = { false, true, true, true };

        public Task() {
            try {
                bw1 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
                        "/Users/luolong/Code/Android/workspace/MultiThreads/A.txt"))));
                bw2 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
                        "/Users/luolong/Code/Android/workspace/MultiThreads/B.txt"))));
                bw3 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
                        "/Users/luolong/Code/Android/workspace/MultiThreads/C.txt"))));
                bw4 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(
                        "/Users/luolong/Code/Android/workspace/MultiThreads/D.txt"))));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void outputA() {

            lock.lock();

            try {
                while (outputThisRound[0]) {
                    System.out.println("outputA begin to await!");
                    cond1.await(); //阻塞A线程
                }

                if (ctl % 4 == 0) {
                    bw1.write("A");
                    bw1.flush();
                } else if (ctl % 4 == 1) {
                    bw4.write("A");
                    bw4.flush();
                } else if (ctl % 4 == 2) {
                    bw3.write("A");
                    bw3.flush();
                } else if (ctl % 4 == 3) {
                    bw2.write("A");
                    bw2.flush();
                }

                outputThisRound[0] = true;
                outputThisRound[1] = false;

                System.out.println("outputA signal outputB!");
                cond2.signal(); //唤醒B线程
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }

        public void outputB() {

            lock.lock();

            try {
                while (outputThisRound[1]) {
                    System.out.println("outputB begin to await!");
                    cond2.await();
                }

                if (ctl % 4 == 0) {
                    bw2.write("B");
                    bw2.flush();
                } else if (ctl % 4 == 1) {
                    bw1.write("B");
                    bw1.flush();
                } else if (ctl % 4 == 2) {
                    bw4.write("B");
                    bw4.flush();
                } else if (ctl % 4 == 3) {
                    bw3.write("B");
                    bw3.flush();
                }

                outputThisRound[1] = true;
                outputThisRound[2] = false;
                System.out.println("outputB signal outputC!");
                cond3.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        public void outputC() {
            lock.lock();
            try {
                while (outputThisRound[2]) {
                    System.out.println("outputC begin to await!");
                    cond3.await();
                }

                if (ctl % 4 == 0) {
                    bw3.write("C");
                    bw3.flush();
                } else if (ctl % 4 == 1) {
                    bw2.write("C");
                    bw2.flush();
                } else if (ctl % 4 == 2) {
                    bw1.write("C");
                    bw1.flush();
                } else if (ctl % 4 == 3) {
                    bw4.write("C");
                    bw4.flush();
                }

                outputThisRound[2] = true;
                outputThisRound[3] = false;
                System.out.println("outputC signal outputD!");
                cond4.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        public void outputD() {
            lock.lock();

            try {
                while (outputThisRound[3]) {
                    System.out.println("outputD begin to await!");
                    cond4.await();
                }

                if (ctl % 4 == 0) {
                    bw4.write("D");
                    bw4.flush();
                } else if (ctl % 4 == 1) {
                    bw3.write("D");
                    bw3.flush();
                } else if (ctl % 4 == 2) {
                    bw2.write("D");
                    bw2.flush();
                } else if (ctl % 4 == 3) {
                    bw1.write("D");
                    bw1.flush();
                }

                outputThisRound[3] = true;
                outputThisRound[0] = false;
                ctl++;
                System.out.println("outputD signal outputA!");
                cond1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

以上。

如果大家还有其他更好的方法,欢迎一起讨论:-)

Created by Long Luo at 2015-04-09 22:14:02 @Shenzhen, China.
Completed By Long Luo at 2015-04-09 23:46:29 @Shenzhen, China.
时间: 2024-08-13 08:58:44

Google多线程面试题: 4个线程向4个文件里写入数据, 每个线程只能写一个值的相关文章

多线程(三) 实现线程范围内模块之间共享数据及线程间数据独立(ThreadLocal)

ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.JDK 1.2的版本中就提供java.lang.ThreadLocal,使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量. 1.下图和辅助代码解释ThreadLocal的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据. 2.每个线程调用全局ThreadLocal

Java多线程面试题整理

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒.Java在语言层面对多线程提供了卓越的支持,它也是一个很好的卖点. 2) 线程和进程有什么区别? 线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务.不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间.别把它和栈内存搞混,每个线

死磕 java线程系列之自己动手写一个线程池

欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写的线程池如何测试? 简介 线程池是Java并发编程中经常使用到的技术,那么自己如何动手写一个线程池呢?本文彤哥将手把手带你写一个可用的线程池. 属性分析 线程池,顾名思义它首先是一个"池",这个池里面放的是线程,线程是用来执行任务的. 首先,线程池中的线程应该是有类别的,有的是核心线程,有

JAVA学习第二十八课(多线程(七))- 停止线程和多线程面试题

重点掌握 /* * wait 和 sleep 区别? * 1.wait可以指定时间也可以不指定 * sleep必须指定时间 * 2.在同步中,对CPU的执行权和锁的处理不同 * wait释放执行权,释放锁    sleep释放执行权,不释放锁 */ //同步里具备执行资格的线程不止一个,但是能得到锁的只有一个,所以能执行的也只有一个 一.停止线程的方式 不可能让线程一直在运行,所以需要让线程停止 1.定义循环结束标记 一般而言,线程运行代码都是循环的,只要控制了循环就可以结束任务 2.使用int

多线程面试题系列(5):经典线程同步 关键段CS

上一篇提出了一个经典的多线程同步互斥问题,本篇将用关键段CRITICAL_SECTION来尝试解决这个问题.本文首先介绍下如何使用关键段,然后再深层次的分析下关键段的实现机制与原理.关键段CRITICAL_SECTION一共就四个函数,使用很是方便.下面是这四个函数的原型和使用说明. 函数功能:初始化 函数原型: void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection); 函数说明:定义关键段变量后必须先初始化. 函数

多线程面试题系列(16):多线程十大经典案例之一 双线程读写队列数据

前十五篇中介绍多线程的相关概念,多线程同步互斥问题(第四篇)及解决多线程同步互斥的常用方法--关键段.事件.互斥量.信号量.读写锁.为了让大家更加熟练运用多线程,将会有十篇文章来讲解十个多线程使用案例,相信看完这十篇后会让你能更加游刃有余的使用多线程. 首先来看第一篇--第十六篇 多线程十大经典案例之一 双线程读写队列数据 <多线程十大经典案例之一双线程读写队列数据>案例描述: MFC对话框中一个按钮的响应函数实现两个功能:显示数据同时处理数据,因此开两个线程,一个线程显示数据(开了一个定时器

Google面试题—有四个线程1、2、3、4。线程1的功能就是输出1,线程2的功能就是输出2,以此类推.........现在有四个文件ABCD

分类: windows编程 C++ 2012-10-27 19:56 3410人阅读 评论(1) 收藏 举报 有四个线程1.2.3.4.线程1的功能就是输出1,线程2的功能就是输出2,以此类推.........现在有四个文件ABCD.初始都为空.现要让四 个文件呈如下格式:A:1 2 3 4 1 2....B:2 3 4 1 2 3....C:3 4 1 2 3 4....D:4 1 2 3 4 1....请设计程序. [cpp] view plaincopy #include <stdio.h

50个Java多线程面试题

不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java 语言一个重要的特点就是内置了对并发的支持,让 Java 大受企业和程序员的欢迎.大多数待遇丰厚的 Java 开发职位都要求开发者精通多线程技术并且有丰富的 Java 程序开发.调试.优化经验,所以线程相关的问题在面试中经常会被提到. 在典型的 Java 面试中, 面试官会从线程的基本概念问起, 如:为什么你需要使用线程, 如何创建线程,用什么方式创建线程比较好(比如:继承 thread 类还是调用 Runnable 接口),

JAVA多线程面试题

1.Thread 类中的start() 和 run() 方法有什么区别? Thread.start()方法(native)启动线程,使之进入就绪状态,当cpu分配时间该线程时,由JVM调度执行run()方法. 当你调用start()方法时你将创建新的线程,并且执行在run()方法里的代码.但是如果你直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码. 2.Java中Runnable和Callable有什么不同? Runnable和Callable都代表那些要在不同的线程中执行的任