Java多线程十问十答(一)

前言:

随着处理器的生产工艺的提升,处理器越来越趋向多核化。为了给用户更好的体验,充分利用处理器多核的特点,使用多线程技术是必然的选择。但是,Java关于线程的知识非常多又不容易理解,使用不当又特别容易出事故等情况。可以说,多线程是程序员前进路上额绊脚石。下面,我们将分享一下,多线程的相关知识:

正文:

Q&A1:什么是进程,什么是线程?

进程:进程是程序一次动态执行的过程,它是程序执行过程中分配和管理资源的基本单位

线程:线程是进程创建的,它是被系统独立调度和分派的基本单位,线程不拥有系统资源,只拥有一点必不可少的资源,但它可以与同一个进程的其它线程共享进程的全部资源。

Tips一个程序至少一个进程,一个进程至少一个线程。

Q&A2:线程安全是什么?

进程中有多个线程在同时运行,而这些线程可能会同时运行一段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

Q&A3:创建线程的方式有那些?

继承Thread类(Thread implements Runnable)创建线程:重写run方法;

实现Runnable接口创建线程:实现run方法,接口实现类的实例对象作为Thread构造函数的形式参数;

实现Callable接口和Future创建线程:实现call方法接口,实现类的实例作为FutureTask构造函数的形参,FutureTask类的实例作为Thread构造函数的形参。

Q&A4:Runnable和Callable的区别?

Runnable提供run(),不会抛出异常,只能在run方法内部处理异常,而Callable提供call(),直接抛出Exception异常;

Runnable的run()无返回值,而Callable的call()提供返回值,用来表示任务运行的结果;

Runnable可以作为Thread构造器的参数,通过开启新的线程来执行,也可以通过线程池来执行,而Callable只能通过线程池执行。

Q&A5:线程的生命周期状态图:

Java多线程十问十答(一)_第1张图片

Q&A6:线程有那种五种状态?

1) 新建状态(New):

当线程对象被创建后,线程进入了新建状态,如:Thread  t = new Thread();

2) 就绪状态(Runnable):

当调用线程对象的start()方法,即t.start();,线程进入就绪状态。处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。

3) 运行状态(Running):

当线程获得CPU时间后,调用run()方法,线程进入运行状态。

Tips就绪状态是进入到运行状态的唯一入口(线程要想进入运行状态就必须处于就绪状态)。

4) 阻塞状态(Blocked):

处于运行状态的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,线程进入阻塞状态。

  • 阻塞状态:

等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

同步阻塞:运行的线程在获取对象的同步锁(synchronized)时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

其他阻塞:运行的线程执行sleep()或join()方法,发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时,join()等待线程终止或者超时,I/O处理完毕时,线程重新转入就绪状态。(注:sleep()是不会释放持有的锁

5) 死亡状态(Dead):

线程执行完成、因异常退出、调用stop()方法,线程结束进入死亡状态。

Q&A7:如何停止一个线程?

i. 程序运行结束,线程自动结束;

ii. 使用interrupt()与interruptted()/isinterrupted()结合+抛异常法/Return法;[推荐异常法,异常可以上抛,异常信息可以传播]

iii. 使用volatile变量标识终止正常运行的线程 + 抛异常法/Return法

iv. stop()暴力停止线程;[不推荐使用,原因:i.可能使一些清理工作得不到完成;II.对锁定对象进行'解锁',数据得不到同步处理,出现数据不一致]。

Q&A8:wait()、notify()和notifyAll()的作用是什么?

1)  wait():

作用:使当前执行代码的线程进行等待。

特点:

i. 该方法会释放锁,把线程放入“等待池”;

ii. 该方法处的代码会停止执行,直到接到通知或被中断;

iii. 调用该方法前,线程必须获得对象级别锁(synchronized方法或代码块中调用),否则抛出IllegalMonitorStateException异常。

2)  notify():

作用:通知那些可能等待该对象对象锁的其他线程,如果有多个线程等待,随机挑选一个;

特点:

i. 执行notify()的线程,不会立马释放该对象锁,要等执行notify()方法的线程的程序执行完毕,也就是退出synchronized代码后,才会释放锁;

ii. 一个调用notify()的线程对应一个调用wait()的线程;

iii. 调用该方法前,线程必须获得对象级别锁,否则抛出IllegalMonitorStateException异常;

3) notifyAll():

作用:使所有正在等待同一共享资源的线程从等待状态退出,进入可运行状态(有可能随机、有可能按照优先级)

4)Tips

Java强制wait()/notify()的调用必须要在一个“同步块”中,避免出现lost wake up问题。lost wake up问题可以见《为什么wait()方法要放在同步块中?》

Q&A9:join()的作用是什么?

作用:等待线程对象销毁,然后才让其它线程执行;主要作用是同步,使线程之间的并行执行变为串行执行(排队运行)。

  • join(long)sleep(long)的区别:

join(long)内部使用wait(long)方法实现的,它具有释放锁的特点,但sleep(long)不会释放锁。

Q&A10:yield()和join()的区别?

yield()放弃当前的CPU资源,将它让给其他的任务去占用CUP执行时间。表明该线程没有在做一些紧急的事情。

join()参考上题。

Q&A11:sleep()和wait()的区别?

sleep是让线程休眠,到时间后会继续执行,wait是等待,需要唤醒再继续执行;

sleep是Thread线程类的方法,wait是Object顶级类的方法;

sleep可以在任何地方使用,wait只能在同步方法或者同步块中使用

sleep,wait调用后都会暂停当前线程并让出CPU的执行时间,但不同的是sleep不会释放当前持有的对象的锁资源,到时间后会继续执行,wait会放弃所有锁并需要notify/notifyAll后重新获取到对象锁资源后才能继续执行。

Q&A12:sleep(0)的作用?

Thread.Sleep(0) 并非是要线程挂起0毫秒,是让当前线程被冻结了一下(暂时放弃CPU),让其他线程有机会优先执行,就相当于一个让位动作。

 

你可能感兴趣的:(多线程)