Java基础知识_多线程入门
一、初识多线程
1.1 介绍进程
讲到线程,又不得不提进程了。wwww
进程大家是了解滴,在windows下打开任务管理器,可以发现我们操作系统上运行的程序都是进程。

进程的定义:
进程是程序的一次执行,进程是一个程序及其数据在处理机上顺序执行时所发生的活动,进程是具有独立功能的程序在一个数据集合上运行的过程,它是系统进行资源调度分配的一个独立单位
进程是系统进行资源分配和调度的独立单位,每一个进程都有它自己的内存空间和系统资源。
1.2 回到线程
那系统已经有了进程这么个概念了,进程已经是可以进行资源调度和分配了,为什么还要哦线程呢?
为使程序能够并发执行,系统必须进行以下一系列的操作
1、创建进程:系统在创建一个进程的时候,必须为它分配其必须的、除处理机以外的资源,如内存空间、IO设备、以及相应的PCB
2、撤销进程:系统在撤销进程的时候,又必须先对其所占有的资源执行回收操作,然后再撤销PCB
3、进程切换:对进程进行上下文切换时,需要保留当前进程的CPU环境,设置新选中的进程的CPU环境,因而须花费不少的处理机时间。

可以看到进程实现多处理机环境下的进程调度分派和切换,都需要花费较大的时间和空间开销。
引入线程主要是为了提高系统的执行效率,减少处理机的空转时间和调度切换的时间,以及便于系统管理。使OS具有更好的并发性
简单来说:进程实现多处理非常耗费CPU的资源,而我们引入线程使作为调度和分派的基本单位(取代进程的部分基本功能【调度】)
那么,线程在哪呢?????
比如听着网易云音乐,点击菜单时还可以响应。
也就是说:在同一个进程内又可以执行多个任务,而这每一个任务我们就可以看作是一个线程。
所以说:一个进程会有1个或多个线程!
1.3 进程与线程
我们可以总结:
进程作为资源分配的基本单位
线程作为资源调度的基本单位,最程序的执行单位,执行路径。是程序使用CPU的最基本的单位
线程有3个基本状态
执行、就绪、阻塞
线程有5个基本操作
派生、阻塞、激活、调度、结束

线程的属性:
1、轻型实体
2、独立调度和分派的基本单位
3、可并发执行
4、共享进程资源
线程有两个基本类型
1、用户级线程:管理过程全部由用户程序完成,操作系统内核心只对进程进行管理。
2、系统级线程(核心级线程)由操作系统内核进行管理,操作系统内核给应用程序提供相应的系统调用和应用程序接口API,以使用户程序可以创建、执行以及撤销线程。
值得注意的是:多线程的存在不是为了提高程序的执行速度。其实是为了提高程序的执行速度,其实是为了提高应用程序的使用率,程序的执行其实都是在抢CPU的资源,CPU的执行权。多个进程在抢这个资源,而其中某个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权
1.4 并行与并发
并行
并行指的是同一时刻发生两个或多个事件
并行是在不同实体上的多个事件
并发
并发性是指同一个时间间隔内发生两个或多个事件
并发是指在同一个实体上的多个事件
1.5 Java实现多线程
我们回到Java中,看看Java是如何实现多线程的
Java实现多线程使用的是Thread这个类,我们来看看Thread类的顶部注释
1 /** 2 * A <i>thread</i> is a thread of execution in a program. The Java 3 * Virtual Machine allows an application to have multiple threads of 4 * execution running concurrently. 5 * <p> 6 * Every thread has a priority. Threads with higher priority are 7 * executed in preference to threads with lower priority. Each thread 8 * may or may not also be marked as a daemon. When code running in 9 * some thread creates a new <code>Thread</code> object, the new 10 * thread has its priority initially set equal to the priority of the 11 * creating thread, and is a daemon thread if and only if the 12 * creating thread is a daemon. 13 * <p> 14 * When a Java Virtual Machine starts up, there is usually a single 15 * non-daemon thread (which typically calls the method named 16 * <code>main</code> of some designated class). The Java Virtual 17 * Machine continues to execute threads until either of the following 18 * occurs: 19 * <ul> 20 * <li>The <code>exit</code> method of class <code>Runtime</code> has been 21 * called and the security manager has permitted the exit operation 22 * to take place. 23 * <li>All threads that are not daemon threads have died, either by 24 * returning from the call to the <code>run</code> method or by 25 * throwing an exception that propagates beyond the <code>run</code> 26 * method. 27 * </ul> 28 * <p> 29 * There are two ways to create a new thread of execution. One is to 30 * declare a class to be a subclass of <code>Thread</code>. This 31 * subclass should override the <code>run</code> method of class 32 * <code>Thread</code>. An instance of the subclass can then be 33 * allocated and started. For example, a thread that computes primes 34 * larger than a stated value could be written as follows: 35 * <hr><blockquote><pre> 36 * class PrimeThread extends Thread { 37 * long minPrime; 38 * PrimeThread(long minPrime) { 39 * this.minPrime = minPrime; 40 * } 41 * 42 * public void run() { 43 * // compute primes larger than minPrime 44 * . . . 45 * } 46 * } 47 * </pre></blockquote><hr> 48 * <p> 49 * The following code would then create a thread and start it running: 50 * <blockquote><pre> 51 * PrimeThread p = new PrimeThread(143); 52 * p.start(); 53 * </pre></blockquote> 54 * <p> 55 * The other way to create a thread is to declare a class that 56 * implements the <code>Runnable</code> interface. That class then 57 * implements the <code>run</code> method. An instance of the class can 58 * then be allocated, passed as an argument when creating 59 * <code>Thread</code>, and started. The same example in this other 60 * style looks like the following: 61 * <hr><blockquote><pre> 62 * class PrimeRun implements Runnable { 63 * long minPrime; 64 * PrimeRun(long minPrime) { 65 * this.minPrime = minPrime; 66 * } 67 * 68 * public void run() { 69 * // compute primes larger than minPrime 70 * . . . 71 * } 72 * } 73 * </pre></blockquote><hr> 74 * <p> 75 * The following code would then create a thread and start it running: 76 * <blockquote><pre> 77 * PrimeRun p = new PrimeRun(143); 78 * new Thread(p).start(); 79 * </pre></blockquote> 80 * <p> 81 * Every thread has a name for identification purposes. More than 82 * one thread may have the same name. If a name is not specified when 83 * a thread is created, a new name is generated for it. 84 * <p> 85 * Unless otherwise noted, passing a {@code null} argument to a constructor 86 * or method in this class will cause a {@link NullPointerException} to be 87 * thrown. 88 * 89 * @author unascribed 90 * @see Runnable 91 * @see Runtime#exit(int) 92 * @see #run() 93 * @see #stop() 94 * @since JDK1.0 95 */
通过顶部注释我们就可以发现,创建多线程有两种方法:
继承Thread,重写run方法
实现Runnable接口,重写run方法
1.6 Java实现多线程需要注意的细节
不要将run和start搞混了
run和start的区别
run仅仅是封装了被线程执行的代码,直接调用是普通方法
start首先启动了线程,然后再由Java虚拟机去调用线程的run方法
jvm虚拟机启动的是单线程还是多线程的
是多线程的。不仅仅是启动main线程,还至少会启动垃圾回收线程的,不然谁帮你回收不用的内存
那么,既然有两种方式实现多线程,我们一般使用哪一种
一般我们使用实现Runnable接口
可以避免Java中的单继承的限制
应该将并发运行任务和运行机制解耦,因此我们选择Runnable接口这种方式
二、总结
简单入门。

浙公网安备 33010602011771号