JAVA多线程基础总结
Contents
如何创建并运行Java线程
Java线程类也是一个Object类,它的实例都继承自Java.lang.Thread 或者其子类。可以用如下方式在Java中创建一个线程1
2Thread thread = new Thread();
thread.start();// 执行该线程
编写线程运行的代码分为两种方式,一种是创建Thread子类的一个实例并且重写run()方法,第二种是创建类的时候实现Runnable 接口。
- 创建子类
1 | //创建Thread子类的一个实例并重写run方法,run方法会在调用start()方法之后被执行。 |
一旦线程启动后start()方法就会立即返回,而不会等待到run()方法执行完毕后才返回,就好像run()方法是在另外一个CPU上执行一样,当run方法执行后,将会打印出字符running。
- 实现runnable接口
runnable的中文意思即为任务,顾名思义,通过实现runnable接口,我们定义了一个子任务,然后将子任务交给Thread去执行。这种方式必须将runnable作为Thread类的参数。
新建一个实现了Java.lang.Runnable接口的类的实例,实例中的方法可以被线程调用。
1 | public class MyRunnable implements Runnable |
为了使线程可以执行run()方法,需要在Thread类的构造函数中传入MyRunnable 的实例对象
1 | Thread thread =new Thread(new MyRunnable()); |
Thread 类中有方法 getName 来得到当前线程的名字
但是实现runnable接口的子类并不是Thread的子类。所以没有getName方法。
通用做法:
1 | String name = Thread.currentThread().getName(); |
注意
1 因为线程操作的不确定性,所以主线程有可能最先执行完,那么此时其他线程不会受到任何影响,并不会随着主线程的结束而结束。 但是主线程一般还是最后完成,因为执行各种关闭动作。
2 一个Java程序启动至少会启动两个线程,每当使用Java命令执行一个类时,都会启动一个jvm,每一个jvm实际都是在OS中启动了一个线程,再加上垃圾收集线程,所以Java程序运行时,至少会启动两个线程,一个是主线程,一个是垃圾收集线程。
线程的状态
创建(new)状态: 准备好了一个多线程的对象
就绪(runnable)状态: 调用了
start()
方法, 等待CPU进行调度运行(running)状态: 执行
run()
方法阻塞(blocked)状态: 暂时停止执行, 可能将资源交给其它线程使用
终止(dead)状态: 线程销毁
创建一个进程后,并不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源等)只有线程运行的条件都满足了,才进入就绪状态。进入就绪状态也不代表获得了CPU的执行时间,因此只有等到获得了CPU的执行时间,线程才真正的进入运行状态。
线程类中的方法
静态方法
currentThread()
方法返回代码段正在被哪个线程调用的信息。
1 | public class Run1 |
sleep() 方法 线程的休眠
sleep()方法作用实在指定的毫秒数内让当前正在执行的线程休眠,这个正在执行的线程就是this.currentThread()返回的线程。
sleep 相当于让线程睡眠,交出CPU,让CPU执行其他任务,但是sleep()方法不会释放锁,如果当前线程持有某个对象的锁,即使调用sleep,其他线程也无法访问这个对象
如果调用了sleep方法,必须捕获InterruptedException 异常或者向上抛出。当线程睡眠时间满后,不一样会立即得到执行,因此此时可能CPU正在执行其他任务。所以相当于让线程阻塞
1 | sleep(long millis) //毫秒 |
yield()方法 线程的礼让
调用yield会让当前线程交出CPU权限,让CPU执行其他的线程,跟sleep一样,也不释放锁,但是yield不能控制具体的交出CPU的时间,另外,yield只能让相同优先级的线程获取CPU执行时间的机会。调用yield不会让线程阻塞,只会重新回到就绪状态。
对象方法
start()方法 start用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。
run()方法 run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。
getId()方法获取线程的唯一标识
isAlive()方法 判断当前线程是否处在活动状态。
join()方法 很多情况下,主线程创建并启动了线程,如果子线程要进行大量的耗时运算,主线程往往早于子线程结束前结束,这时,如果主线程想等待子线程执行完成后再结束,就是对子线程用join方法,让他进行强制执行,作用就是等待线程对象销毁。用法为对象.join(),然后这个对象就先执行,执行完后再执行其他进程。
在线程操作中,可以使用join方法让一个线程强制执行,线程强制执行期间,其他线程无法运行,必须等待此线程完成以后才可以继续执行。
join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程,比如在线程b中调用了a的join方法,直到线程a执行完毕后,才会继续执行线程b.
1 | class Mythread implements Runnable |
Object类方法
wait()方法导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。
notify()方法 选择一个在该对象上调用wait方法的线程,解除其等待状态。该方法只能在同步方法或同步块内部调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。
任何对象都可以调用这两个方法,当在一个对象实例上调用wait()方法后,当前线程就会在这个对象上等待,比如,在线程A中,调用了obj.wait()方法,那么线程A就会停止继续运行,而转为等待状态,等待到何时结束呢,线程A会一直等待到其他线程调用了obj.notify()方法为止
object.wait()方法并不是随时可以调用的,它必须包含在对应的Synchronized语句中,wait和notify都需要先获得目标对象的一个监视器.
Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?
答:sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。 wait()会释放锁,sleep不释放任何资源
停止线程
三种方式。
- 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
- 使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend及resume一样,都是作废过期的方法,使用他们可能产生不可预料的结果。
- 使用interrupt方法中断线程,但这个不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。
中断线程
线程中断不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出,至于目标线程接到通知后如何处理,则完全由目标线程自行决定.
当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态。
Author: corn1ng
Link: https://corn1ng.github.io/2018/01/19/Java多线程基础总结/
License: 知识共享署名-非商业性使用 4.0 国际许可协议