操作员在机房里来回调度资源,以及计算机同一时刻只能运行一个程序,在程序输入的过程中计算机处于空闲状态。而当时计算机是非常昂贵的,人们为了减少这种资源浪费。就采用了批处理系统来解决
批处理操作系统虽然能够解决计算机空闲问题,但是当某一个作业因为等待磁盘或者其他I/O操作暂停时,那CPU就只能阻塞直到该I/O的完成,对于CPU操作密集型的程序,I/O操作相对较少,因此浪费的时间也很少但是对于I/O操作较多的场景来说,CPU的资源是属于严重浪费的
多道程序设计的出现解决了这个问题,就是把内存分为几 个部分,每一个部分放不同的程序。当一个程序需要等待 I/O 操作完成时。那么 CPU 可以切换执行内存中的另外一 个程序。如果内存中可以同时存放足够多的程序,那 CPU 的利用率可以接近 100%。 在这个时候,引入了第一个概念- 进程, 进程的本质是一个 正在执行的程序,程序运行时系统会创建一个进程,并且 给每个进程分配独立的内存地址空间保证每个进程地址不 会相互干扰。同时,在 CPU 对进程做时间片的切换时,保 证进程切换过程中仍然要从进程切换之前运行的位置出开 始执行。所以进程通常还会包括程序计数器、堆栈指针。
有了进程以后,可以让操作系统从宏观层面实现多应用并 发。而并发的实现是通过 CPU 时间片不端切换执行的。对 于单核 CPU 来说,在任意一个时刻只会有一个进程在被 CPU 调度
有了进程以后,为什么还会发明线程?
如何创建多线程
在Java中,有多种方式来实现多线程。继承Thread类,实现Runnable接口,使用ExcutorService,Callable,Future实现带返回结果的多线程,线程池ThreadPool
线程可以合理利用多核心CPU资源,提高程序吞吐量
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();
如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个 Runnable 接口
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
有的时候,我们可能需要让一步执行的线程在执行完成以 后,提供一个返回值给到当前的主线程,主线程需要依赖 这个值进行后续的逻辑处理,那么这个时候,就需要用到 带返回值的线程了。Java 中提供了这样的实现方式
public class CallableDemo implements Callable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService= Executors.newFixedThreadPool(1);
CallableDemo callableDemo=new CallableDemo();
Future 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;
@Override
public String toString() {
return "Request [name=" + name + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
RequestProcessor
public interface RequestProcessor {
void processRequest(Request request);
}
PrintProcessor
public class PrintProcessor extends Thread implements RequestProcessor {
LinkedBlockingQueue requests = new LinkedBlockingQueue();
private final RequestProcessor nextProcessor;
public PrintProcessor(RequestProcessor nextProcessor){
this.nextProcessor = nextProcessor;
}
public void run(){
while(true){
try {
Request request = requests.take();
System.out.println("printdata:"+request.getName());
nextProcessor.processRequest(request);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void processRequest(Request request) {
// TODO Auto-generated method stub
requests.add(request);
}
}
SaveProcessor
public class SaveProcessor extends Thread implements RequestProcessor{
LinkedBlockingQueue requests = new LinkedBlockingQueue();
@Override
public void run() {
while (true) {
try {
Request request=requests.take();
System.out.println("begin save requestinfo:"+request);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//处理请求
public void processRequest(Request request) {
requests.add(request);
}
}
Main
public class Main {
PrintProcessor printProcessor;
protected Main(){
SaveProcessor saveProcessor=new SaveProcessor();
saveProcessor.start();
printProcessor=new PrintProcessor(saveProcessor);
printProcessor.start();
}
private void doTest(Request request){
printProcessor.processRequest(request);
}
public static void main(String[] args) {
Request request=new Request();
request.setName("陈·风暴烈酒");
new Main().doTest(request);
}
}
线程的生命周期
线程一共有六种状态(NEW、RUNNABLE、BLOCKED、 WAITING、TIME_WAITING、TERMINATED)
NEW:初始状态,线程被构建,但是还没有调用start方法
RUNNABLE:运行状态,Java线程把操作系统的就绪和运行两种状态统一称为“运行中”
BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了CPU使用权阻塞也分几种情况
等待阻塞:运行的线程执行wait方法,jvm会把当前线程放入等待队列
同步阻塞:运行的线程在获取对象同步锁时,若该同步锁被其他线程锁占用了,那么jvm会把当前的线程放到锁池中
其他阻塞:运行的线程执行Thread.sleep或者t.join方法,或者发出了I/O请求时JVM会把当前线程设置为阻塞状态,当sleep结束,join线程终止,io处理完毕则线程恢复
TIME_WAITING:超时等待状态,超时以后自动返回
TERMINATED:终止状态,表示当前线程执行完毕