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日志并进行多维度分析。
相关文章
|
5天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
42 17
|
15天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
17天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
17天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
16天前
|
并行计算 算法 安全
面试必问的多线程优化技巧与实战
多线程编程是现代软件开发中不可或缺的一部分,特别是在处理高并发场景和优化程序性能时。作为Java开发者,掌握多线程优化技巧不仅能够提升程序的执行效率,还能在面试中脱颖而出。本文将从多线程基础、线程与进程的区别、多线程的优势出发,深入探讨如何避免死锁与竞态条件、线程间的通信机制、线程池的使用优势、线程优化算法与数据结构的选择,以及硬件加速技术。通过多个Java示例,我们将揭示这些技术的底层原理与实现方法。
68 3
|
18天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
42 3
|
18天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
102 2
|
15天前
|
缓存 安全 Java
【JavaEE】——单例模式引起的多线程安全问题:“饿汉/懒汉”模式,及解决思路和方法(面试高频)
单例模式下,“饿汉模式”,“懒汉模式”,单例模式下引起的线程安全问题,解锁思路和解决方法
|
15天前
|
Java 调度
|
18天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
48 1