java基础之多线程略解
java中的多线程是非常重要的一个知识点,下面我们就来简单的介绍下多线程的相关知识以及相关方法。
并发与并行
并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于间时间隔较短,使人感觉两个任务都在运行。
线程的五种状态
新建,就绪,运行,阻塞,死亡
JVM启动的是多线程
JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。
java中线程的实现方式
1,继承Thread
使用步骤:
1.定义类继承Thread
2.重写run方法
3.把新线程要做的事写在run方法中
4.创建线程对象
5.开启新线程, 内部会自动执行run方法
2,实现Runnable接口
实现步骤:
1.定义类实现Runnable接口
2.实现run方法
3.把新线程要做的事写在run方法中
4.创建自定义的Runnable的子类对象,创建Thread对象传入Runnable
5.调用start()开启新线程, 内部会自动调用Runnable的run()方法
3,通过Callable<V>接口和 FutureTask<V>创建线程
实现步骤:
1.创建Callable接口的实现类,设置V的类型,并实现call()方法,该call()方法将作为线程执行体,并且有返回值,返回值类型为V类型。
2.创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
3.使用FutureTask对象作为Thread对象的target创建并启动新线程。(能作为target是因为FutureTask实现了Runnale接口)
4.调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
例:
以上三种创建方式的区别
继承Thread : 由于子类重写了Thread类的run(), 当调用start()时直接找子类的run()方法。
实现Runnable : 构造函数中传入了Runnable的引用, 有个成员变量记住了它, 调用run()方法时内部判断成员变量Runnable的引用是否为空。
通过Callable接口和FutureTask创建线程:FutureTask是Runnbale的实现类,所以与Runnable相同,并且也是调用run方法,只是FutureTask将run方法重写了,在run方法中调用call方法。
继承Thread
好处是:可以直接使用Thread类中的方法,代码简单。
弊端是:如果已经有了父类,就不能用这种方法。
实现Runnable接口
好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,代码更灵活。
弊端是:不能直接使用Thread中的方法,需要先获取到线程对象后,才能得到Thread的方法,代码复杂。
通过Callable接口和FutureTask创建线程
好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,代码更灵活。并且call方法有返回值,还能抛出异常。
弊端是:不能直接使用Thread中的方法,需要先获取到线程对象后,才能得到Thread的方法,代码复杂相对于实现Runnable接口的方式更加复杂。
获取线程名字和设置名字
通过Thread的getName()方法获取线程对象的名字。
通过setName(String)方法可以设置线程对象的名字。
通过构造函数可以传入String类型的名字。
每个线程系统都会默认分配个名字,主线程:main,子线程thread-0 ....
获取当前线程的对象
Thread.currentThread()方法用于获取当前线程对象。
线程的其它方法
Thread.sleep(毫秒), 控制当前线程休眠若干毫秒。
setDaemon(), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出。特点:男守护女,女的死,男的也不想活了。
join(), 当前线程暂停, 等待指定的线程执行结束后,当前线程再继续,记住哪个线程调用该方法,则先执行哪个线程。
join(int), 可以等待指定的毫秒之后继续。
join方法详解:join方法的原理就是调用相应线程的wait方法进行等待操作的,例如A线程中调用了B线程的join方法,则相当于在A线程中调用了B线程的wait方法(这里是将B线程对象设为A线程在等待池中的标识),当B线程执行完(或者到达等待时间),B线程会自动调用自身的notifyAll方法(通过标识可以在等待池中找到A线程然后唤醒)唤醒A线程,从而达到同步的目的。
yield() 线程让出cpu。
setPriority()设置线程的优先级,默认优先级是5,最小优先级1,最高优先级10,可以设置2,3,4,Thread里面有静态常量。
interrupt(),中断线程。
interrupt方法详解:Thread.interrupt 的作用不是中断线程,而是通知线程应该中断了,
如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。此时我们可以在catch中关闭线程。
如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true。被设置中断标志的线程将继续正常运行,不受影响。这种情况下,如果我们需要关闭线程,我们可以通过Thread.currentThread.isInterrupted()来判断中断标志是否为true,如果是,那么我们可以进行关闭线程处理,如下图所示,当中断标志为true时,循环结束,run方法执行完毕。
new Thread(new Runnable() {
public void run() {
while(!Thread.currentThread().isInterrupted()) {
System.out.println("线程的中断标志变为false时,执行我");
}
System.out.println("线程的中断标志变为true时,执行我");
}
}).start();
要记住的是,isInterrupted()返回中断标志后,会将中断标志设为false。
关闭线程的几种方式
1.设置退出标志,使线程正常退出,也就是当run()方法完成后线程终止,如果是循环,那么我们可以设置当不符合循环条件时跳出循环,或者在循环代码中用if判断条件,不符合则通过break退出循环。
2.使用interrupt()方法中断线程,至于如何关闭上面已经说明。
3.使用stop方法强行终止线程(不推荐使用,Thread.stop, Thread.suspend, Thread.resume 和Runtime.runFinalizersOnExit 这些终止线程运行的方法已经被废弃,使用它们是极端不安全的!)。