java多线程--几个多线程面试题小结

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:   自学了一段时间的多线程知识,尝试了做了几个编程题,发现想象中很简单的功能,自己真写起来要花费远超自己想象的功夫,知识点易学,不易用啊. 面试题1:编写程序实现,子线程循环10次,接着主线程循环20次,接着再子线程循环10次,主线程循环20次,如此反复,循环50次.

  自学了一段时间的多线程知识,尝试了做了几个编程题,发现想象中很简单的功能,自己真写起来要花费远超自己想象的功夫,知识点易学,不易用啊.

面试题1:编写程序实现,子线程循环10次,接着主线程循环20次,接着再子线程循环10次,主线程循环20次,如此反复,循环50次.

package com.wang.reflect;
//编写功能类,实现子线程和主线程的功能
class Function{
    private boolean flag=false;
    //子线程要实现的功能
    public synchronized void sub(){
        while(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
               
        for(int i=0;i<10;i++){
            //for循环内定义子线程的功能,这里简单的假设为打印一句话,主线程同理
            System.out.println("sub"+i);
        }
        
        flag=true;
        this.notify();
    }
    //主线程要实现的功能
    public synchronized void main(){
        while(!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for(int i=0;i<20;i++){
            System.out.println("main"+i);
        }
        
        flag=false;
        this.notify();
    }
    
}

public class Demo01 {
  
    public static void main(String[] args) {
         final Function f=new Function();
        new Thread(
                new Runnable(){

                    @Override
                    public void run() {
                        for(int i=0;i<50;i++){
                            f.sub();
                        }
                    }
                
                }
                ).start();
        
        for(int i=0;i<50;i++){
            f.main();
        }
    }
}

  JDK1.5以后,出现了Lock和condition,Lock类似于synchronized功能,用来进行线程同步,Condition功能类似于Object类中的wait和notify方法,用于线程间的通信.上面的代码可以用Lock和Condition来改进,如下:

 

package com.wang.reflect;

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

//编写功能类,实现子线程和主线程的功能
class Function{
    private boolean flag=false;
    
    Lock lock=new ReentrantLock();
    Condition con=lock.newCondition();
    //子线程要实现的功能
    public  void sub(){
        lock.lock();
        try {
            
            while(flag){
                try {
                    con.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            
            for(int i=0;i<10;i++){
                //for循环内定义子线程的功能,这里简单的假设为打印一句话,主线程同理
                System.out.println("sub"+i);
            }
            
            flag=true;
            con.signal();
        } finally{
            lock.unlock();
        }
    }
    //主线程要实现的功能
    public synchronized void main(){
        lock.lock();
        try {
            while (!flag) {
                try {
                    con.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < 20; i++) {
                System.out.println("main" + i);
            }
            flag = false;
            con.signal();
        } finally{
            lock.unlock();
        }
    }
    
}


public class Demo01 {

    
    
    public static void main(String[] args) {
         final Function f=new Function();
        new Thread(
                new Runnable(){

                    @Override
                    public void run() {
                        for(int i=0;i<50;i++){
                            f.sub();
                        }
                    }
                
                }
                ).start();
        
        for(int i=0;i<50;i++){
            f.main();
        }
    }
}

 

 

 

面试题2:设计四个线程,其中两个线程每次对变量i加1,另外两个线程每次对i减1.

package com.wang.reflect;

/**
 * 设计四个线程,其中两个线程每次对变量i加1,另外两个线程每次对i减1.
 * @author Administrator
 *
 */
public class Demo02 {

    private int  i=0;
    public static void main(String[] args) {
        Demo02 demo=new Demo02();
        Add add = demo.new Add();
        Sub sub = demo.new Sub();
        for(int i=1;i<=2;i++){
            new Thread(add,"线程"+i).start();
            new Thread(sub,"线程"+i).start();
        }
    }
    
    
    //定义一个内部类Add,实现功能每次对i加一
    class Add implements Runnable{

        @Override
        public void run() {
            for(int i=0;i<10;i++){
                addOne();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
    }
    //定义一个内部类Sub,实现功能每次对i减1
    class Sub implements Runnable{

        @Override
        public void run() {
            for(int i=0;i<10;i++){
            subOne();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            }
        }
        
    }
    
    public synchronized void addOne() {
        i++;
        System.out.println(Thread.currentThread().getName()+"加一的值为:"+i);
    }
    public synchronized void subOne(){
        i--;
        System.out.println(Thread.currentThread().getName()+"减一的值为:"+i);
    }
}

面试题3:自己编写代码,实现生产者-消费者模型功能.内容自由发挥,只需要表达思想.

  代码中,自定义一个学生类,有name和age属性,属于共享对象,生产者负责为studnet对象赋值,消费者负责打印出student对象的name和age的值,当生产者赋值完以后通知消费者来打印,消费者打印完以后,通知生产者重新设置.

package com.wang.reflect;

//学生实体类作为共享资源
class Student {
    private String name;// 姓名
    private int age;// 年龄
    boolean flag;// 标记变量,判断当前学生对象是否已创建赋值好

    //生产者的功能  ,为studnet对象赋值
    public synchronized void set(String name, int age) {

        if (this.flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.name = name;
        this.age = age;

        this.flag = true;
        this.notify();
    }

    //消费者的功能,打印sutdent对象的内容
    public synchronized void get() {
        if (!this.flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(name + ":::" + age);

        this.flag = false;
        this.notify();
    }

}

// 模拟生产者线程类
class SetStudent implements Runnable {

    // 共享资源s
    private Student s;
    private int x = 0;

    public SetStudent(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            if (x % 2 == 0) {
                s.set("郭靖", 27);
            } else {
                s.set("黄蓉", 18);
            }
            x++;
        }
    }

}

// 模拟消费者线程类
class GetStudent implements Runnable {

    // 共享资源s
    private Student s;

    public GetStudent(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            s.get();
        }
    }

}

// 测试类
public class Demo03{

    public static void main(String[] args) {
        Student s = new Student();

        SetStudent ss = new SetStudent(s);
        GetStudent gs = new GetStudent(s);

        Thread t1 = new Thread(ss, "生产者");
        Thread t2 = new Thread(gs, "消费者");

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

}

面试题4: 现有的程序代码模拟产生了16个日志对象,并且需要运行16秒才能打印完这些日志,请在程序中增加4个线程去调用parseLog()方法来分头打印这16个日志对象,程序只需要运行4秒即可打印完这些日志对象。

原始代码如下:

public class Test {
        
        public static void main(String[] args){
            
            System.out.println("begin:"+(System.currentTimeMillis()/1000));
            /*模拟处理16行日志,下面的代码产生了16个日志对象,当前代码需要运行16秒才能打印完这些日志。
            修改程序代码,开四个线程让这16个对象在4秒钟打完。
            */
            for(int i=0;i<16;i++){  //这行代码不能改动
                final String log = ""+(i+1);//这行代码不能改动
                {
                         Test.parseLog(log);
                }
            }
        }
        
        //parseLog方法内部的代码不能改动
        public static void parseLog(String log){
            System.out.println(log+":"+(System.currentTimeMillis()/1000));
            
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }        
        }
        
    }

改写代码如下:

package com.wang.reflect;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Demo03 {
    
    public static void main(String[] args){
        //定义一个线程共享的队列容器,可以使得数据由队列的一端输入,从另外一端输出
        final BlockingQueue<String> queue=new ArrayBlockingQueue<String>(16);
        for(int i=0;i<4;i++){
            new Thread(new Runnable(){

                @Override
                public void run() {
                    while(true){
                        try {
                            parseLog(queue.take());
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                
            }).start();
        }
        
        System.out.println("begin:"+(System.currentTimeMillis()/1000));
        /*模拟处理16行日志,下面的代码产生了16个日志对象,当前代码需要运行16秒才能打印完这些日志。
        修改程序代码,开四个线程让这16个对象在4秒钟打完。
        */
        for(int i=0;i<16;i++){  //这行代码不能改动
            final String log = ""+(i+1);//这行代码不能改动
            {
                     try {
                        queue.put(log);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
            }
        }
    }
    
    //parseLog方法内部的代码不能改动
    public static void parseLog(String log){
        System.out.println(log+":"+(System.currentTimeMillis()/1000));
        
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }        
    }
    
}

 

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
9天前
|
数据采集 Java Linux
面试大神教你:如何巧妙回答线程优先级这个经典考题?
大家好,我是小米。本文通过故事讲解Java面试中常见的线程优先级问题。小明和小华的故事帮助理解线程优先级:高优先级线程更可能被调度执行,但并非越高越好。实际开发需权衡业务需求,合理设置优先级。掌握线程优先级不仅能写出高效代码,还能在面试中脱颖而出。最后,小张因深入分析成功拿下Offer。希望这篇文章能助你在面试中游刃有余!
33 4
面试大神教你:如何巧妙回答线程优先级这个经典考题?
|
5天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
52 14
|
8天前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
38 13
|
9天前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。
|
12天前
|
缓存 安全 Java
面试中的难题:线程异步执行后如何共享数据?
本文通过一个面试故事,详细讲解了Java中线程内部开启异步操作后如何安全地共享数据。介绍了异步操作的基本概念及常见实现方式(如CompletableFuture、ExecutorService),并重点探讨了volatile关键字、CountDownLatch和CompletableFuture等工具在线程间数据共享中的应用,帮助读者理解线程安全和内存可见性问题。通过这些方法,可以有效解决多线程环境下的数据共享挑战,提升编程效率和代码健壮性。
44 6
|
28天前
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
73 16
|
25天前
|
Java 程序员 调度
Java 高级面试技巧:yield() 与 sleep() 方法的使用场景和区别
本文详细解析了 Java 中 `Thread` 类的 `yield()` 和 `sleep()` 方法,解释了它们的作用、区别及为什么是静态方法。`yield()` 让当前线程释放 CPU 时间片,给其他同等优先级线程运行机会,但不保证暂停;`sleep()` 则让线程进入休眠状态,指定时间后继续执行。两者都是静态方法,因为它们影响线程调度机制而非单一线程行为。这些知识点在面试中常被提及,掌握它们有助于更好地应对多线程编程问题。
58 9
|
9天前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
38 17
|
18天前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
50 26
|
2月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
235 2