JAVA基础知识总结:十八

一、进程和线程
1.进程
是一个程序的运行状态和资源占用的描述

进程的特点:
a.独立性:不同的进程之间是独立的,相互之间资源不共享
b.动态性:进程在系统中不是静止不动的,而是一直活动的
c.并发性:多个进程可以在同一个处理器上同时进行,互不影响

多进程:一个操作系统可以运行多个应用程序

2.线程
线程是进程的组成部分,一个进程可以有多个线程,每个线程用来处理一个指定的子任务

举例:打开酷狗软件-------》这是一个进程
播放歌曲/刷新歌词------》两个线程(并发的)

线程的执行是抢占式的,多个线程可以在一个进程中并发执行,其实质是CPU在不同的线程之间进行快速的切换,也就是说,当前运行的线程在任何时候都有可能被挂起,以便于别的线程去执行对应的任务,同样的,被挂起的线程随时有可能争抢到时间片,继续执行

多线程:在一个进程中,多个线程同时进行
应用:一个浏览器可以同时下载多张图片
一个服务器可以同时响应多个用户请求


3.进程和线程之间的关系
a.一个程序运行后至少有一个进程
b.一个进程可以包含多个线程,但至少需要有一个线程,否则进程是没有意义的

为什么要选用多线程编程而不选用多进程呢?/线程相对于进程的优点?
a.进程间资源不能进行共享,但是线程之间可以共享资源
b.系统如果要创建进程的话,需要为这个进程重新分配系统资源,而创建线程的话则相对容易的多,因此使用线程处理并发任务比进程的效率高
c.Java中内置了多线程的功能支持,简化了多线程编程


二、线程的实现
1.继承自Thread类
Thread类是所有线程类的父类,实现了对线程的抽取和封装
1>使用Thread类创建并开启线程的步骤:
a.定义一个类,继承自Thread类,重写该类的run方法,该run方法的方法体就代表了线程需要完成的任务,因此,run方法体也被称为线程执行体
b.创建子类的对象,即相当于创建了一个线程
c.需要使用start方法手动开启线程


关于线程的执行,需要注意的问题:
a.如果一个线程中的所有的任务都处理完了,那么这个线程会自动停止(正常情况)
b.如果在一个线程a中开辟了子线程a0,a1,a2.....那么线程a停止后,在这个线程中开辟的子线程会全部停止
c.多个线程并发执行,其实就是在争抢CPU时间片


2.实现接口Runnable
使用实现Runnable接口的方式创建并开启线程的步骤:
a.定义一个类,这个类实现Runnable接口,需要重写对应的run方法,run方法的方法体同样是线程的执行体
b.创建实现了Runnable接口对应类的对象,并以此实例作为Thread类的target对象
c.手动调用start方法开启线程

两种方式实现线程的比较:
1.实现Runnabel接口
a.自定义的类只是实现了Runnable接口,同时还可以去继承其他的类
b.多个线程可以共享同一个target对象,所以非常适合多个相同的线程来处理同一份资源的情况
弊端:不直观,如果要获取当前正在运行的线程,只能通过Thread.currentThread()
2.继承Thread类
直观,如果要访问正在运行的线程,除了可以通过Thread.currentThread()方式之外,还可以使用super关键字

弊端:因为线程类已经继承了Thread类,所以不能再去继承其他的类(单继承)


实际上,大多数的多线程应用都采用实现Runnable接口的方式实现(推荐使用匿名内部类)


三、线程的生命周期
对象的生命周期:从一个对象被实例化到这个对象被销毁的过程中,这个对象经历的种种状态

举例:人的生命周期:出生---婴儿---儿童---少年---青年----中年---老年---死亡


对于线程,当一个线程被创建并启动之后,它既不是一启动就进入了执行状态,也不是一直处于执行状态,在线程的生命周期中,同样的也会经历各种过程(在一个进程中,多个线程可以并发,争抢CPU时间片)


New(新生):线程被实例化,但是还没有开始执行
Runnable(就绪):没有争抢到时间片
Running(运行):争抢到了时间片,开始执行线程中的任务
Blocked(阻塞):线程再执行的过程中遇到突发状况,使得其他的线程争抢去了时间片,被阻塞的线程会等待合适的时机重新进入就绪状态
Dead(死亡):线程终止
a.run方法执行完毕,线程正常结束
b.直接调用该线程的stop方法强制终止这个线程(这种做法比较危险,死锁)

四、线程的常用方法
1.设置线程的优先级
可以通过设置优先级来改变线程抢到时间片的概率,优先级高的线程抢到时间片的概率比较高,可以获得更多的执行机会

默认情况下,每个线程的优先级都与创建它的的父线程具有相同的优先级
setPriority(),所传的参数范围1~10,默认为5,对应的数值越大,说明优先级越高,这个方法的设置一定要在start之前

2.使得线程休眠
使得当前正在执行的线程休眠一段时间,释放时间片,导致线程进入阻塞状态
sleep(5000);5000代表的是毫秒
设置了sleep就相当于将当前线程挂起5s,这个操作跟线程的优先级无关,当对应的时间到了之后,还会再执行,
如果只设置了优先级的话,等于给设置了优先级的线程设置冲突,那么被挂起的是优先级比较低的那个线程

3.中断线程
interrupt()
interrupt只是改变一个线程的状态,并不是真正的停止一个线程
如果线程处于阻塞状态(sleep,join),则中断状态被清除
如果一个线程没有处于阻塞状态,这时调用interrupt将不起作用,否则,会引起InterruptedException异常(该线程预先准备好处理此状况)

实际看到的效果:停止了线程

在线程的执行体中,可以通过 boolean isInterrupted() 来测试线程是否已经中断。


4.线程合并
join()
优先执行被合并进来的线程,执行完合并进来的线程之后,再执行原来的线程


5.后台线程
setDeamon()
又被称为守护线程或者精灵线程
特征:如果所有的前台线程都死亡,后台线程会自动死亡

这个方法的调用需要在start之前

6.线程让步
yield()
yield方法是一个和sleep方法相似的方法
它可以让当前正在执行的线程暂停,但他不会阻塞该线程,它只是将该线程转入就绪状态,完全会出现的一个情况是:当某个线程调用了yield方法暂停之后,线程调度器可能会将该线程立马调起来进入执行状态
实际上,只有优先级相同或者优先级更高的线程才有可能获取执行的机会

yield方法和sleep方法的区别:
a.sleep方法暂停当前线程之后,会给其他线程执行的机会的,但是跟线程的优先级没有关系,yield只有优先级相同或者优先级更高的线程才有可能获取执行的机会
b.sleep会将一个线程转入阻塞状态,但是yield不会,只是做出了让步
c.sleep会有InterruptedException的异常,但是yield没有
d.sleep可以有更好的可移植性,通常不要依靠yield来控制并发的线程

原文地址:https://www.cnblogs.com/lidar/p/7743279.html