Java-线程
一、进程和线程
进程:进程是程序的一次动态执行过程,它对应了从代码加载、执行到执行完毕的一个完整过程
线程:线程是比进程更小的执行单位
举列子:当你打开一个App可以看成一个实现进程,当打开一个App里的一个功能,比如播放音乐,病毒扫描就是实现一个线程。
线程调度:计算机操作系统为了提高程序的运行效率,当一个线程空闲时就会撤下这个线程,而会让其他线程执行!
二、并行和并发
并行:针对进程的,同一个时刻发生多个事件,比如你用酷狗音乐播放音乐的同时,打开浏览器看这篇博客。
并发:针对线程的,在一段时间内发生多个事件
三、Java多线程的实现
有两类方法
- 继承Thread方法,重写run() 方法
- 实现Runnable接口,重写run()方法
继承Thread,重写run()方法
Thread是java.lang包下的一个类,当用户操作Thread类时,Thread类创建一个线程并交付给操作系统进行调度。
步骤:
(1)创建一个类继承(extend)Thread 类,定义线程的构造函数;
(2)用需在此线程中执行的代码覆盖 Thread 类的 run()方法;
(3)用关键字 new 创建所定义的线程类的一个实例化对象;
(4)调用该对象的 start()方法启动线程。
实现Runnable接口,重写run()方法
很多情况下,一个类已经继承了其他类,由于java单继承的限制,该类无法继承Thread类,java语言便提供了Runnable接口,从而使得不继承Thread的情况下也能创建新的线程!
这个方式常用!因为可以避免java中单继承的限制,可以将并行 运行任务和运行机制 解耦
步骤:
(1)创建一个类实现(implements)Runnable 接口;
(2)用需在此线程中执行的代码覆盖 Thread 类的 run()方法;
(3)在类中定义一个 Thread 类对象;
(4)利用public Thread(Runnable target) 实例化定义对象
【public Thread(Runnable target)】 -----利用一个实现了 Runnable 接口参数对象 Target中所定义的 run()方法,来初始化或覆盖新创建的线程对象的 run()方法。
(5)调用该对象的 start()方法启动线程。
敲黑板画细节:
关于run()方法和start()的正确理解
run():仅仅是封装被线程执行的代码
start():首先启动了线程,然后再用jvm去调用该线程的run()方法
四、线程的状态和生命周期
五种状态
新建状态–>就绪状态–>运行状态–>阻塞状态–>死亡状态
下面这段话来自java语言程序设计,写的非常清楚!
(1)新建状态(Newborn)
当一个 Thread 类或其子类对象被声明并用 new 关键字来实例化时,新生的线程对象处于新建状态。此时它已经被初始化,并拥有了相应的内存空间和其它资源,但还未开始执行。
(2)就绪状态(Runnable)
处于新建状态的线程被启动后(即执行了 start()方法后),进入了就绪状态。此时它已具备运行条件,被放入线程队列排队等待执行,一旦轮到它来使用处理器资源时,就可以脱离创建它的主线程独立开始自己的生命周期了。至于何时真正执行,取决于线程的优先级和在线程队列中的位置。另外,原来处于阻塞状态的线程被解除阻塞后也将进入就绪状态。
(3)运行状态(Running)
当就绪状态的线程被调度并获得处理器资源时,便进入运行状态。当线程对象被调度执行时,它将自动调用本对象的 run()方法,从第一句开始顺序执行。
(4)阻塞状态(Blocked)
一个正在执行的线程在某些特殊情况下(如被人为挂起或需要执行费时的输入输出操作时),将让出 CPU 并暂时中止自己的执行,进入阻塞状态。阻塞时它不能进入排队队列,只有引起阻塞的原因被消除,线程才可以转入就绪状态,重新进到线程队列中排队等待 CPU资源,以便从原来终止处开始继续执行。
引起阻塞的方法有 Wait()、Sleep()、Yield()等。
等待阻塞:运行的线程执行 wait()方法,JVM会把该线程放入等待池中。( wait会释放持有的锁);
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中;
其他阻塞:运行的线程执行 sleep()或 join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当 sleep()状态超时、 join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意, sleep是不会释放持有的锁);
(5)死亡状态(Dead)
处于死亡或终止状态的线程不具有继续运行的能力。其原因有两个:
一个是正常运行的线程完成了它的全部工作,即执行完了 run()方法的最后一个语句并退出;
另一个是线程被提前强制性的终止,如通过执行 stop()方法强行终止当前线程(该方法目前已不建议使用)。
五、线程的控制
线程调度
计算机操作系统为了提高程序的运行效率,当一个线程空闲时就会撤下这个线程,而会让其他线程执行!
Java中提供一个线程调度管理器来监控启动后进入就绪状态的所有线程,并通过优先级来确定运行的顺序。
线程的优先级预定义为10,1和5,分别对应三个优先级,默认为5即NORM_PRIORITY,可以用getPriority()和返回线程的优先级,也可以用setPriority()改变线程的优先级。
挂起线程
- wait:使得线程进入等待状态,直到被另一个线程唤醒
- sleep:线程睡眠,sleep()可用于让线程沉睡若干毫秒,sleep(1000)为沉睡一秒钟,而1秒后,线程会自动苏醒,并继续执行后续的代码,sleep()方法并非一定能运行成功,当线程在挂起状态,由于某种原因打断了,它会抛出一个类型InterruptException的异常。
- yield:功能是暂停当前正在执行的线程方法,让其他线程先执行,需要注意的是,yield()方法只会给相同优先级线程一个执行机会
- join:功能:是将几个并行的线程合并成单线程执行。
- interrupt:一个线程运行时,另一个线程可以调用对应的 Thread对象的interrupt()方法中断它。(但它仅仅是通知,什么时候终端还是要我们自己控制的)
结束线程
stop()方法:程序中需要强制终止某线程的生命周期时可以使用stop()方法。
stop方法可以由线程在自己的run()方法中调用,也可以其他线程在执行方法中调用。
stop()方法将会使线程由其他状态进入死亡状态。
但不推荐 使用stop()方法来强制终止线程,因为它将会导致错误发生。例如,如果一个线程正在操作共享数据段,操作过程没有完成即被执行stop()方法,将会导致数据的不完整!
destroy()方法:程序可以使用destroy()方法来强制结束线程,但是这个方法在结束线程后,将不会释放锁,因此也不推荐使用!
参数控制法:由于stop()和的 destroy()方法在java新版本中已不建议使用,可以使用一个指示run()方法必须退出的标志来停止一个线程。具体步骤如下:
- 编写一个类继承Thread类
- 在这个类中增加一个布尔变量,并将其值初始化为false。
- 覆盖start()方法,首先将布尔变量的值置为true,然后调用super()方法,start()方法。
- 提供一个public方法 halt(),它将变量置为false。
- 在run()方法中使用类似下面的while循环:
public void run ( ){
while(布尔参数){
线程要执行的代码
}
如果这个线程的halt()方法被调用,则会将布尔参数的值置为假,从而引起run()方法终止执行,从而结束该线程!
六、线程的同步
锁机制
synchronized(同步)锁:
synchronized就是一个关键字,它能将代码块锁起来
synchronized是一种互斥锁,一次只能允许一个线程进入被锁住的代码块(方法)
lock显式锁
lock显式锁就是一个接口
lock显式锁比synchronized锁的优点就是灵活性高,缺点是需要手动释放锁。
公平锁:线程按照它们发出的请求顺序来获取锁
非公平锁:synchronizated锁和lock显式锁都是非公平锁,线程发出请求时可以插队来获取锁。
死锁:
- 当前线程拥有其他线程需要的资源
- 当前线程等待其他线程已拥有的资源
- 都不放弃拥有的资源
原因及解决的办法
- 线程间交错执行 解决:以固定的顺序加锁
- 执行某方法时就需要持有锁且不释放
解决:缩减同步代码块的范围,最好仅操作共享变量时才加锁 - 永久等待 解决:使用定时锁
七、常用的线程方法
不需要特别记忆,需要用的时候过来查询即可
方法 | 含义 |
---|---|
start() | 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 |
run() | 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。 |
setName() | 改变线程名称,使之与参数 name 相同。 |
setPriority() | 更改线程的优先级。 |
setDaemon() | 将该线程标记为守护线程或用户线程。 |
join() | 等待该线程终止的时间最长为 millis 毫秒 |
interrupt() | 中断线程。 |
isAlive() | 测试线程是否处于活动状态。 |
yield() | 暂停当前正在执行的线程对象,并执行其他线程。 |
sleep() | 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。 |