字节面试:项目中,什么地方用到了多线程?

发布时间:2023-05-04 10:28:56

你好,我是田哥

多线程在面试中一直是加分项,如果不能回答,很可能会影响下一场比赛,严重影响面试结果。

接下来,我们来谈谈我们项目中可以使用多线程的场景。

让我们来看看多线程使用的主要目的:

1、吞吐量:你做WEB,容器帮你做多线程,但他只能帮你做请求级别。简单来说,可能是一个请求一个线程。或者多个请求一个线程。如果是单线程,只能同时处理一个用户的请求。

2、可伸缩性:也就是说,您可以通过增加CPU核数来提高性能。如果是单线程,则使用单核程序直到死亡。当然,没有办法通过增加CPU核数来提高性能。

举个简单的例子:

假设有一个请求,该请求服务端的处理需要执行三个非常缓慢的IO操作(如数据库查询或文件查询),正常顺序可能是(括号代表执行时间):

  1. 读取文件1 (10ms)
  2. 处理1的数据(1ms)
  3. 读取文件2 (10ms)
  4. 处理2的数据(1ms)
  5. 读取文件3 (10ms)
  6. 处理3的数据(1ms)
  7. 整合1、2、3的数据结果 (1ms)

单线程总共需要34ms。

如果你把ab放在这个请求内,、cd、ef分别分三个线程去做,只需要12ms。

因此,多线程并非没有多大用处,而是你通常应该善于找到一些优化点。然后评估方案是否应使用。假设上述问题仍然相同:但每个步骤的执行时间不同。

  1. 读取文件1 (1ms)
  2. 处理1的数据(1ms)
  3. 读取文件2 (1ms)
  4. 处理2的数据(1ms)
  5. 读取文件3 (28ms)
  6. 处理3的数据(1ms)
  7. 整合1、2、3的数据结果 (1ms)

单线程总共需要34ms。

如果仍按上述划分方案(上述方案与桶原理相同,耗时取决于最慢线程的执行速度),则为第三线程,执行29ms。所以最后一个请求需要30ms。与不使用单线程相比,节省4ms。但是,线程调度切换也有可能花费1、2ms。因此,该方案的优势并不明显,但也提高了程序的复杂性。不太值得。

所以现在的优化点,不是第一个例子,任务分割多线程完成。但是优化文件3的读取速度。可能是缓存和减少一些重复读取。

首先,假设所有用户都要求这个请求,这实际上相当于所有用户都需要阅读文件3。想想看,100人提出了这个请求,相当于你花在阅读这个文件上的时间是28×100=2800ms了。所以,如果你缓存了文件,只要读取了第一个用户的请求,第二个用户就不需要读取。从内部访问非常快,可能不到1ms。

伪代码:

public class MyServlet extends Servlet{    private static Map<String, String> fileName2Data = new HashMap<String, String>();    private void procesfile3(String fName){        String data = fileName2Data.get(fName);        if(data==null){            data = readFromFile(fName);    //耗时28ms            fileName2Data.put(fName, data);        }        //process with data    }}

它看起来不错。建立文件名和文件数据的映射。如果读取map中已经存在的数据,则无需读取文件。

但问题是,Servlet是并发的,这将导致一个非常严重的问题,一个死循环。因为,当Hashmap并发修改时,它可能会导致循环链表的组成!!!(具体来说,您可以自己阅读Hashmap源代码)如果您没有接触到太多的线程,您可能会发现服务器没有要求或巨大的卡,也不知道发生了什么!

好吧,用ConcurrentHashmap,就像他的名字一样,他是一个安全的Hashmap,可以很容易地解决问题。

public class MyServlet extends Servlet{    private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>();    private void procesfile3(String fName){        String data = fileName2Data.get(fName);        if(data==null){            data = readFromFile(fName);    //耗时28ms            fileName2Data.put(fName, data);        }        //process with data    }}

这真的解决了问题吗?这样,只要用户访问文件a,另一个用户想访问文件a,他就会从filename2data中获取数据,然后不会造成死循环。

然而,如果你认为这已经结束了,那么你认为多线程太简单了,骚年!你会发现,当1000个用户第一次访问相同的文件时,他们读取了1000个文件(这是最极端的,可能只有几百个)。What the fuckin hell!!!

代码错了吗?我就这样过我的生活吗?

好好分析一下。Servlet是多线程的,所以

public class MyServlet extends Servlet{    private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>();    private void procesfile3(String fName){        String data = fileName2Data.get(fName);        ///“偶然”-- 1000个线程同时到这里,同时,data也被发现为null        if(data==null){            data = readFromFile(fName);    //耗时28ms            fileName2Data.put(fName, data);        }        //process with data    }}

上面注释的“偶然”是完全有可能的,所以这样做还是有问题的。

因此,您可以简单地包装一个任务来处理它。

public class MyServlet extends Servlet{    private static ConcurrentHashMap<String, FutureTask> fileName2Data = new ConcurrentHashMap<String, FutureTask>();    private static ExecutorService exec = Executors.newCacheThreadPool();    private void procesfile3(String fName){        FutureTask data = fileName2Data.get(fName);        ///“偶然”-- 1000个线程同时到这里,同时,data也被发现为null        if(data==null){            data = newFutureTask(fName);            FutureTask old = fileName2Data.putIfAbsent(fName, data);            if(old==null){                data = old;            }else{                exec.execute(data);            }        }        String d = data.get();        //process with data    }         private FutureTask newFutureTask(final String file){        return  new FutureTask(new Callable<String>(){            public String call(){                return readFromFile(file);            }             private String readFromFile(String file){return "";}        }    }}

以上所有代码都是直接在bbs打出来的,不能保证直接运行。

多线程最多的场景:web服务器本身;各种特殊服务器(如游戏服务器);

常见的多线程应用场景:

  • 后台任务,如:定期向大量(100w以上)用户发送邮件;
  • 发微博、记录日志等异步处理;
  • 分布式计算

好了,我做了一个网站,在网上刷面试题:

网站:www.woaijava.cc

字节面试:项目中使用多线程的地方?_前端'

上一篇 git操作手册
下一篇 yuv rgb 互转 公式 及算法

文章素材均来源于网络,如有侵权,请联系管理员删除。

标签: Java教程Java基础Java编程技巧面试题Java面试题