进程与线程概叙
要想了解多线程必须要知道线程的原理,而线程是依赖于进程的存在故而要先把进程弄明白
何为进程?
通过任务管理器(win : ctrl + alt + .)可以看到进程的存在。
而通过观测发现,只有运行的程序才会出现进程
进程建议理解为正在运行的程序
进程是系统进行资源分配和调用的独立单位.每个进程都有它的内存空间与系统资源多进程的意义
单进程的计算机只能做一件事,而现在的计算机可以做多件事:一边打游戏一边听音乐...
可以在一个时间段执行多个任务,CPU的占用会上升.
故而有个好处是提高CPU使用率。
问题 : (单核为例)
一边玩游戏一边听音乐是同时进行的吗?
不是,单cpu在某时间点只能做一件事
在玩游戏或听音乐时,CPU做程序的高效切换让我们觉得是同时运行
当然多核能保证同时进行(双核保证两个,类推...)何为线程?
再一个进程内可以执行多个任务,而每个任务可以看成一个线程.以纸牌为例,游戏以开始就在不断计时
线程 : 是程序(进程)的执行单元,执行路径。是程序使用CPU的最基本单位
单线程 : 如果程序只有一条执行路径
多线程 : 如果程序有多条执行路径多线程的意义
多线程的存在,不是提高程序的执行速度.其实是为了提高应用程序的使用率。
程序的执行其实都是在抢CPU的资源,CPU的执行权。
多个进程是在抢这个资源,而其中某一个进程如果执行路径比较多,就有更高的几率抢到CPU执行权
无法保证哪个线程能在哪个时刻抢到,所以线程的执行有随机性并行与并发
并行是逻辑上同时发生,指在某个时间段内同时运行多个程序
并发是物理上同时发生,指在某个时间点内同时运行多个程序Java程序运行原理
java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。
该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
思考 : jvm的启动是单线程还是多线程?
多线程,以main方法为例.除了主线程应该还有gc等..java如何实现多线程程序
由于线程依赖于进程,故而要先创建一个进程
而进程是由系统创建的,所以要调用系统功能创建一个进程.
Java是不能直接调用系统功能的,故而无法直接实现多线程程序
但Java可以调用C/C++写好的程序实现多线程程序
由C/C++调用系统功能创建进程,java再调用这样的东西,然后提供一些类供我们使用就可以实现实现多线程的方式
有两种常用的方式:
一、继承Thread类并重写run方法,然后创建实例并启动Thread
二、实现Runnable接口并重写run方法
Runnable三、使用Callable<V>接口{% post_link Java/Thread/Callable Callable %}
线程调度
一个CPU在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。
分时调度模型 : 所有线程轮流使用CPU使用权,平均分配线程占用CPU的时间片
抢占式调度模型 : 优先让优先级高的CPU使用,如果优先级相同,那么随机选择一个,优先级高获得的时间片也比较多
java采用抢占式调度模型线程生命周期
创建(实例) : 创建线程对象
就绪 : 有执行资格,没有执行权
运行 : 有执行资格,有执行权
阻塞 : 由于一些操作让线程处于该状态。没有执行资格,没有执行权
而另一些操作却可以把它激活,激活后处于就绪状态
死亡 : 线程变成垃圾,等待被回收
面试的时候一般给出图解,容易让人理解,以下是最简易图解线程安全问题
将共享数据一次只让一个线程访问,这样就可以保证数据的准确了方式一 :
使用synchronized解决 : {% post_link Java/Thread/Synchronized 线程同步技术 %}
方式二 :
使用java提供的Lock接口 :Lock锁
死锁问题
同步代码如果出现同步的嵌套,那么就容易出现死锁的问题
在两个或以上的线程在执行过程中,因为争夺资源产生的互相等待现象
在程序设计中,避免这种情况,让代码的嵌套顺序一致
简单案例 :1 | public static void main(String[] args) { |
当play1获得锁A而同时play2获得锁B的时候,就可能死锁
以下是出现死锁的情况 :线程间的通信问题
不同种类的线程针对同一个资源的操作,称为线程间的通信问题
以下是一个简单的生产者-消费者案例等待唤醒机制
线程的状态
常见的线程转换状态有如下这些 :
A: 创建--就绪--运行--死亡
B: 创建--就绪--运行--就绪--死亡
C: 创建--就绪--运行--其他阻塞--就绪--运行--死亡
D: 创建--就绪--运行--同步阻塞--就绪--运行--死亡
E: 创建--就绪--运行--等待阻塞--同步阻塞--就绪--运行--死亡
线程状态简易转换图如下 :线程组
有些时候,我们需要对许多功能相同的线程设置值的时候需要一个个的设置
这个时候java为我们提供了线程组ThreadGroup类,利用它就可以统一的管理线程集合线程组
线程池
程序启动一个新线程的成本是比较高的,因为涉及操作系统的交互.而使用线程池可以很好的提升性能
尤其在程序中要创建大量生存期很短的线程时,更应该考虑线程池的使用
那么该如何设置线程池的大小呢?
这才是最难的地方,如果访问量大或者运算量大则应该设计大一点,反之设计小一些
还涉及到并发访问测试以及压力测试等线程池
定时器
JDK3为我们提供了一个Timer接口
它可以用于执行一些定时的任务
但是,现在的开发中一般使用quartz这个开源框架实现定时器任务线程安全的类回顾
Vector
Hashtable
StringBuffer
但是 Vector与Hasht使用比较少了,在Collections工具类中同步的Collection,List,Map,Set接口的同步集合
public static <T> Collection<T> synchronizedCollection(Collection<T> c)
public static <T> Set<T> synchronizedSet(Set<T> s)
public static <T> List<T> synchronizedList(List<T> list)
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)