第一章 并发编程线程基础
在学习java web的过程中,思考过这样一个问题,如果仅仅是跟着视频做几个项目,是否有资本找到一个较好的工作,结合师兄和师姐找工作的过程,他们说找工作的话,面试官问的问题都很基础,也就突出了一个问题,对于应届生,在没有什么实战项目经验的情况下,面试过程中对于基础知识的考核将会是一个重点。后来又思考了一个问题,我们跟着视频做的web项目,到底有多少的实用价值?我个人感觉是很少,我想的是,即使这个项目用到了很多的技术,什么redis、dubbo、solr等等,但是当面对庞大的请求或者搜索的时候,以现有项目的状况,绝对会出现崩的状况,那么如何实现高并发就是一个问题,这也是我决定进行高并发学习的一个重要原因,同时在有理论加代码的基础上,考虑把高并发结合到web的项目上。
什么是线程?什么是进程?
进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。
例子:其实我们电脑或者手机上每一个软件都是一个进程,例如qq或者office,那么在qq或者office中有许多的功能,其中的每一个功能都是一个线程,比如视频、信息发送、信息接收等等。
操作系统在分配资源时把资源分配给进程,但是CPU的资源比较特殊,CPU资源被分配到线程,因为真正要占CPU运行的是线程。
上图中有三种类型区域:堆、方法区和线程。
堆里面主要存放使用new操作创建的对象实例。
方法区用来存放JVM加载的类、常量以及静态变量等信息。
如上图,在这个进程中,有两个线程,这两个线程共享同一个堆和方法区。同时在每个线程之内有一个程序计数器和栈。
那么程序计数器是什么作用呢?我们知道,CPU的执行方式是时间片轮转方式,在当前的线程CPU时间片使用完毕后要让出CPU,那么为了在下次CPU轮到自己时,继续执行上次中断的任务,我们就要记录上次让出CPU时候线程的执行地址,那么程序计数器就起到了存储该线程让出CPU时的执行地址的作用。
栈的作用就是存储该线程的局部变量,还要存储线程的调用栈帧。
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构。它是虚拟机运行时数据区中的虚拟机栈的栈元素。
栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。
每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机里面从入栈到出栈的过程。
线程的创建与运行
1. 实现Runnable接口的run方法
public static class RunnableTask implements Runnable{
@Override
public void run() {
System.out.println("I am a child thread");
}
public static void main(String[] args){
//创建线程
RunnableTask task = new RunnableTask();
new Thread(task).start();
new Thread(task).start();
}
}
在创建完Thread对象之后,该线程并没有立即执行,在调用了start()
方法之后,线程并没有立即执行,而是进入就绪状态,就绪状态就是该线程获得了除CPU资源以外的其他资源,等道获取CPU资源之后才真正处于运行状态。
2. 继承Thread类并重写run方法
//继承Thread类并重写run方法
public static class MyThread extends Thread{
@Override
public void run(){
System.out.println("I am a child thread");
}
public static void main(String[] args){
//创建线程
MyThread myThread = new MyThread();
//启动线程
myThread.start();
}
}
由于java不支持多继承,所以如果已经继承了Thread类,那么就不能再继承其它的类,这是继承Thread的一个缺点。但是使用继承的一个好处是容易传参
3.FutureTask方法
public static class CallerTask implements Callable<String>{
@Override
public String call() throws Exception {
return "hello";
}
public static void main(String[] args) throws InterruptedException{
//创建异步任务
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
//启动线程
new Thread(futureTask).start();
try {
String s = futureTask.get();
System.out.println(s);
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
FutureTask的方法可以拿到任务的返回值