目录
- 多线程的发展历史
- 线程的应用
- 并发编程的基础
- 线程安全问题
- JMM内存模型
- JMM如何解决原子性、可见性、有序性的问题
- synchronized和volatile
什么情况下应该使用多线程
线程出现的目的是什么?
解决进程中多任务的实时性问题,其实简单来说,也 就是解决“阻塞”的问题;
阻塞:就是程序运行到某个函数或过程后等待某些事件发生而暂时停止 CPU 占用的情况,也就是说会使得 CPU 闲置。
多线程的运用在一些场景如:如对于一个函数中的运算逻辑的性能问题:
我们可以通过多线程 的技术,使得一个函数中的多个逻辑运算通过多线程技术达到一个并行执行, 从而提升性能
多线程解决“等待”问题 例子归纳:
- 通过并行计算提高程序执行性能 ;
- 需要等待网络、I/O 响应导致耗费大量的执行时间,可以采用异步线程的方 式来减少阻塞
tomcat7 以前的 io 模型
多线程的应用场景
客户端阻塞 :如果客户端只有一个线程,这个线程发起读取文件的操作必须等待 IO 流返回,线程(客户端)才能做其他的事 ;
线程级别阻塞 :BIO 客户端一个线程情况下,一个线程导致整个客户端阻塞。
那么我们可以使用多线程,一部分线程在等待 IO 操作返回,其他线程可以继续做其他的事。此时从客户端角度来说,客户端没有闲着。 如图:
如何应用多线程
在 Java 中,有多种方式来实现多线程:
1.继承 Thread 类;
2.实现 Runnable 接口;
3.使用 ExecutorService、Callable、Future 实现带返回结果的多线程。
1.继承 Thread 类创建线程
Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。
操作:启动线程的唯一方法就是通过 Thread 类的 start()实例方法;
start()方法是一个native 方法,它会启动一个新线程,并执行 run()方法。
这种方式实现多线程很简单,通过自己的类直接 extend Thread,并复写 run()方法,就可以启动新线
程并执行自己定义的 run()方法;代码如下:
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
2.实现 Runnable 接口创建线程
如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个Runnable 接口,代码如下:
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
3.实现 Callable 接口通过 FutureTask 包装器来创建 Thread 线程
有的时候,我们可能需要上一步执行的线程在执行完成以后,提供一个返回值给到当前的主线程,主线程需要依赖这个值进行后续的逻辑处理,那么这个时候,就需要用到带返回值的线程了。Java 中提供了这样的实现方式,代码如下:
import java.util.concurrent.*;
public class CallableDemo implements Callable<String> {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(1);
CallableDemo callableDemo = new CallableDemo();
Future<String> future = executorService.submit(callableDemo);
System.out.println(future.get());
executorService.shutdown();
}
@Override
public String call() throws Exception {
int a = 1;
int b = 2;
System.out.println(a + b);
return "执行结果:" + (a + b);
}
}
如何把多线程用得更加优雅
合理的利用异步操作,可以大大提升程序的处理性能,通过阻塞队列以及多线程的方式,实现对请求的异步化处理,提升处理性能
Request 基类
public class Request {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Request{" +
"name='" + name + '\'' +
'}';
}
}
RequestProcessor 接口
public interface RequestProcessor {
void processorRequest(Request request);
}
PrintProcessor 实现 RequestProcessor
import java.util.concurrent.LinkedBlockingQueue;
public class PrintProcessor extends Thread implements RequestProcessor{
//阻塞队列
LinkedBlockingQueue<Request> linkedBlockingQueue=new LinkedBlockingQueue();
private final RequestProcessor nextProcessor;
public PrintProcessor(RequestProcessor nextProcessor) {
this.nextProcessor = nextProcessor;
}
@Override
public void run() {
while(true){
try {
Request request=linkedBlockingQueue.take();
System.out.println("print data:"+request);
nextProcessor.processorRequest(request);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void processorRequest(Request request) {
linkedBlockingQueue.add(request);
}
}