一、为什么我要写并发编程呢?
1、一些公司的面试可能会经常被面试问到关于并发多线程的那些事,但是进了公司,需要用自己学到的东西配合实际情况有很多人还是不怎么了解关于并发的那些事,所以从自己的学习中,读书的积累中,以后会配合公司的一点案例讲述下。
2、我认为在互联网的公司中,关于并发的场景很多吧,所以系统的学习下,和大佬们分享下,哪里写的不对,不好给指出。
二、关于线程的那些事
a、线程和进程之间的关系
1、线程是进程中的一个实体,线程本身不会独立存在的。
2、进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。
3、线程则是进程的一个执行路径
4、一个进程中至少有一个线程,进程中的多个线程共享进程的资源。
5、程序计数器:用来为了记录该线程让CPU时的执行地址的,待再次分配到时间片时线程就可以从自己私有的计数器指定地址继续执行。但是注意的是,如果执行的是native方法,那么pc计数器记录的是undefined地址。只有执行的是java代码的时候pc计数器记录的才是下一条指令的地址。
cpu一般是使用时间片转轮的方式轮询占用的,所以当前的cpu时间片用完之后,要让出cpu,等下一次轮到自己的时候再执行。
6、栈:每个线程都有自己的栈内存,用来存储线程的局部变量。而其他的线程是访问不了的,而且栈还存放线程的调用栈帧。
7、堆:堆是被进程中的所有线程共享的,是进程创建时分配的,堆里面主要存放使用new操作创建出来的对象实例。
8、方法区用来存放JVM加载的类,常量及静态变量等信息,也是线程共享的。
b、操作系统分配资源和cpu资源的分配的区别
操作系统:把资源分配给进程的。
cpu:它是被分配到线程的。
因为真正要占用的CPU是线程,所以说线程是CPU分配的基本单位。
而当我们运行的一个主函数的程序的时候其实就是启动一个JVM的线程,而主函数所在的线程就是这个进程中的一个线程/主线程。
三、线程的创建的方式与运行
实现方式:分别是实现Runnable接口的run方法,继承Thread类并重写run的方法,使用FutureTask方式。
a、继承Thread的方式来创建线程,代码如下:
上面的代码的执行的过程的流程图如下:
使用继承的好处:
1、在run()方法内获取当前的线程直接使用this就可以了,不再用Thread.currentThread()方法
使用继承的坏处:
1、java不支持多继承,如果继承了Thread类,那么就不能再继承其他类了
2、任务与代码没有分离,当多个线程执行一样的任务时需要多份任务代码
3、方便传参数;可以在子类里面添加成员变量;通过set方式设置参数或者可以通过构造函数来进行传递;而如果使用Runnable的方式的话,则只能使用主线程里面被声明的final 的变量;不好的地方是java不支持多继承。
b、使用实现Runnable接口的方式来创建线程,代码如下:
上面的代码的执行流程图是和thread的流程图差不多,这里就不去画了。
但是这里RunnableTest这个类可以继承其他类的,但是上面的两种方法都有一个缺点,就是任务都没有返回值。
c、使用FutureTask的方式来创建线程
运行的结果如下:
上面的代码的执行流程图如下: