简单说说线程和进程的区别?
那为什么要使用线程?平时开发中是否用到线程?
使用线程的最主要的原因是提高系统的资源的利用率。
现在的cpu基本都是多核的,如果只是使用了单线程,也就是使用了一个核心,其他的核心就相当于空闲的。
现实中使用多线程的场景
比如说,我们的系统web服务器用的是Tomcat,Tomcat处理每一个请求都会从线程池边用一个线程进行处理。
又比如说,我们的数据库会用到对应的连接池Druid/C3P0等等。
好,那实际上,开发中自己有没有用过多线程呢?
比如说,跑一个定时任务,该任务的链路执行时间和过程都非常长,我们这边就用一个线程池将该定时任务的请求进行处理。
这样做的好处是:可以及时返回结果给调用方,能够提高系统的吞吐量。
// 请求直接交给线程池来处理
public void push(PushParam pushParam) {
try {
pushServiceThreadExecutor.submit(() -> {
handler(pushParam);
});
} catch (Exception e) {
logger.error("pushServiceThreadExecutor error, exception{}:", e);
}
}
还有就是,我们的系统中用了很多的生产者与消费者模式,会用到多线程去消费队列的消息,来提高速度的。
如果我们的项目中,用到了多线程,那肯定需要考虑线程安全的问题吧。
简单介绍下,什么是线程安全?
在Java的世界里,所谓的线程安全就是多个线程执行某一个类,这个类始终能表现出正确的行为,那么这个类就是线程安全的。
比如,我有一个count变量,在service方法不断的进行累加这个count变量。
假设在相同的条件下,count变量每次执行的结果都是相同的,那我们可以说线程安全的。
下面代码是线程不安全的
public class UnsafeCountingServlet extends GenericServlet implements Servlet {
private long count = 0;
public long getCount() {
return count;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
++count;
// To something else...
}
}
那平时是怎么解决的,或者怎么思考线程安全的问题的?
其实大部分时间我们的代码里面都没有显式去处理线程安全的问题,因为这大部分都交给了框架来做了。
正如上面介绍的:tomcat,druid,springmvc等等。
很多时候,我们判读是否要处理线程安全问题,就看有没有多个线程同时访问一个共享变量。
像springmvc这种,我们日常开发中,不涉及操作同一个变量,那我们就很少需要考虑线程安全的问题。
我个人解决线程安全问题的思路是:
- 能不能保证操作的原子性,考虑atomic包下的类够不够我们使用。
- 能不能保证操作的可见性,考虑volatile关键字够不够我们使用。
- 如果涉及到对线程的控制(比如一次能使用多少个线程,当前线程触发的条件是否依赖其他线程的结果),考虑CountDownLatch/Semaphore等等。
- 如果是集合,考虑java.util.concurrent包下的集合类。
如果是Synchronized无法满足,考虑lock包下的类。
总的来说,就是先判断有没有线程安全的问题,如果存在则根据具体的情况来判断使用什么方式来处理线程安全的问题。
了解死锁吗?(重要点)
造成死锁的原因可以简单概括为:当前线程拥有其他线程需要的资源,当前线程等待其他线程已拥有的资源,都不放弃自己拥有的资源。
避免死锁的方式:固定加锁的顺序,比如我们可以使用Hash值的大小来确定加锁的先后。
- 尽可能缩减加锁的范围,等到操作共享变量的时候才加锁。
- 使用可释放的定时锁