您现在所在位置: 首页 > IT知识库

面试重点考题“线程”问题你会了吗?

发布时间:2022-01-12点击数:

面试研发岗位的小伙伴可能发现了,面试官经常会提问有关“线程”的问题,比如,线程是什么?多线程的常见应用场景是什么?创建线程的方式有哪几种?这些方式分别有什么优点?等等。

线程问题这么受面试考官青睐,重要程度自然不必说啦。今天,我们就来讲一下“线程”的问题。

问题一:线程是什么?

线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中;

一个进程可以并发多个线程,每条线程并行执行不同的任务;

多线程程序设计的好处是提高了程序的执行吞吐率和执行效率。

问题二:多线程的常见应用场景

后台任务,例如:定时向大量用户推送信息例如广告邮件,不胜其扰,大家一定有体会

异步处理,例如:批量I/O操作,发微博、记录日志等

分布式计算,共享稀有资源和平衡负载

web服务器本身,例如:Tomcat

问题三:创建线程有几种方式?

使用多线程第一步就是创建线程,今天给大家分享6种创建线程的方式。

方式一:继承Thread类并重写run()方法


public class ThreadDemo1 extends Thread

 {

       private String name;

       public ThreadDemo1 (String name)

{

this.name=name;

}


 @Override 

public void run() 

System.out.println(this.name+"线程运行了"); 

public static void main(String[] args)

 { 

new ThreadDemo1(“线程一”).start(); 

new ThreadDemo1(“线程二”).start();

}

总结:这是最简单的创建方式,但弊端是占用了唯一一次继承机会,因为Java是单继承,如果这个类必须继承其它类,就无法采用这种创建方式了。


方式二: 实现Runnable接口


public class ThreadDemo2 implements Runnable 

@Override public void run() 

System.out.println(Thread.currentThread().getName() + "线程运行了");

public static void main(String[] args) 

new Thread(“线程1”,new ThreadDemo2()).start(); 

new Thread(“线程2”,new ThreadDemo2()).start(); 

}

总结:这种创建方式最大的优点是代码简洁,只需关注线程代码本身即可;缺点是代码复用性较差。如果线程代码只使用一次,可以考虑这种方式,现在多数使用lambda表达式(JAVA8+)。


方式三: 匿名内部类


public classThreadDemo3 

{

 public static void main(String[] args) 

{ // 第一种Thread匿名子类,重写Trun()方法 

new Thread() {

 @Override public void run() 

{

 System.out.println(" 匿名Thread子类,线程运行了"); 

} }.start(); 

// 第二种Runnable匿名实现类,实现run()方法 

new Thread(new Runnable() { 

@Override public void run()

 { 

System.out.println( " Runnable匿名实现类,线程运行了"); 

} }).start(); 

// 第三种使用lambda表达式,简化代码

new Thread(()->{ 

System.out.println("Lambda表达式线程类,线程运行了"); 

}).start(); 

}

总结:这种创建方式最大的优点是代码简洁,只需关注线程代码本身即可;缺点是代码复用性较差。如果线程代码只使用一次,可以考虑这种方式,现在多数使用lambda表达式(JAVA8+)。


方式四: 使用Timer定时器(java.util.Timer)


public class ThreadDemo4 

{

 public static void main(String[] args)

 {    //创建Timer定时器

Timer timer = new Timer(); 


// 每隔1秒执行一次 

timer.schedule(new TimerTask() 

@Override 

public void run()

 { 

System.out.println(Thread.currentThread().getName() + " is running"); 

}, 0 , 1000); 

}

}

总结:TimerTask实现了Runnable接口,Timer定时器可以安排线程“执行一次”或定期“执行多次”。在实际开发过程中,经常需要周期性的操作,如每几分钟执行某项操作等。对于这类需求,最方便高效的实现方式就是使用java.util.Timer工具类。


方式五: 使用线程池


class MyThread  implements Runnable

{

@Override

public void run() 

{

System.out.println(Thread.currentThread().getName()+“正在运行”);

}

}

public class ThreadDemo5 

public static void main(String[] args) 

      //池中有十个线程

ExecutorService threadPool = Executors.newFixedThreadPool(10); 

for (int i = 0; i < 1000; i++) 

{      

threadPool.execute(new MyThread()); 

}

总结:线程池是多个线程的集合,使用线程池我们不需要自己创建线程,将线程任务提交给线程池即可。线程池有以下两大优点:1.可重用已有的线程继续执行任务,减少线程创建和销毁时造成的损耗从而提高系统响应速度;2.通过对可执行线程进行合理管理,根据系统承受能力调整可运行线程数量的大小等。最佳实践Tomcat服务器处理用户请求的线程,就是使用线程池进行管理。

方式六: 实现Callable接口(java.util.concurrent.Callable)

public class ThreadDemo6 implements Callable  

@Override 

public String call() throws Exception 

{    //实现call方法

System.out.println( " is running"); 

return “线程执行后结果”; 

public static void main(String[] args) throws Exception 

FutureTask task = new FutureTask<>(new ThreadDemon6());

new Thread(task).start(); 

System.out.println("等待线程执行结束");

String result = task.get(); 

System.out.println("线程执行结果:" + result); 

}

总结:Java多线程的核心方法run是没有返回值的,如需run方法执行后的结果,必须等待run方法计算完,无论计算过程多么耗时。而Future模式恰恰能解决这一困境。实现Callabe接口,可以获得线程执行后的结果。


上面介绍了那么多创建线程的方式,总结而言,分为两大类:

继承Thread类并重写run()方法

实现Runnable接口的run()方法


上面的内容,你学会了吗?


彩蛋: 某大型互联网公司面试真题


public class ThreadExam

{

 public static void main(String[] args)

{

new Thread(

()-> { System.out.println("Runnable: 运行" ); }

{

@Override public void run() { System.out.println("Thread: 运行"); } 

}.start();

}



  • 友情链接

关注东软睿道公众号了解更多IT行业资讯

添加东小萌微信
获取更多IT学习资源