某一天你在面试时遇到了线程的相干问题。
面试官:“你晓得有哪几种创立线程的形式吗?”
(此时你的心理流动:哈哈小意思这能难住我,忍住冲动伪装淡定道)
你:“嗯,能够通过实现 Runnable 接口和继承 Thread 类来创立线程。”
面试官:“除了这两种还有其余形式吗?”
你:“emmm...还有吗?”
面试官:“晓得通过实现 Callable 接口与获取 Future 对象来实现吗?”
你:“emmm不晓得...不过当初晓得了嘻嘻”
面试官:“那创立线程池有哪些形式呢?”
你:“能够通过 ThreadPoolExecutor 构造函数或者 Executors 提供的工厂办法来创立”
面试官:“那通过不同的 Executors 工厂办法创立线程池之间有什么区别呢?”
你:“emmm...“
面试官:“那 ThreadPoolExecutor 构造函数中的工作队列和回绝策略别离有哪些呢?”
你:“emmm...“
(此时你的心理流动:QAQ...不面了,把劳资简历还我!)
目录
1、线程的定义
2、线程的6种状态及切换
3、线程的4种创立形式
3.1、实现 Runnable 接口
3.2、继承 Thread 类
3.3、通过 Callable、Future
3.4、通过 JUC 外面的线程池
4、几个常见的线程面试题
注释
1、线程的定义
概念:线程是过程中执行运算的最小单位,是过程中的一个实体,是被零碎独立调度和分派的根本单位,线程本人不领有系统资源,只领有一点在运行中必不可少的资源,但它可与同属一个过程的其它线程共享过程所领有的全副资源。一个线程能够创立和吊销另一个线程,同一过程中的多个线程之间能够并发执行。
2、线程6种状态及切换
Java中线程的状态分为6种,定义在Thread类的State枚举中。
public class Thread implements Runnable { ... public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; } ...}
NEW:初始状态,创立一个线程对象时就是该状态。
RUNNABLE:运行状态,它蕴含了就绪(READY)和运行中(RUNNING)两种状态。当线程对象创立后,调用该对象的 start() 办法就会进入就绪状态(READY)。该状态的线程位于可运行线程池中,期待被线程调度选中,获取 CPU 的使用权,在取得 CPU 工夫片后会变为运行中状态(RUNNING)。
BLOCKED:阻塞状态,示意线程此时阻塞于锁。
WAITING:期待状态,进入该状态的线程须要期待其余线程做出一些特定动作(告诉或中断)。
TIMED_WAITING:超时期待状态,该状态与 WAITING 的不同点在于它能够在指定的工夫后自行返回。
TERMINATED:终止状态,示意该线程曾经执行完。
留神下图状态之间的切换。
3、线程的四种创立形式
3.1、实现 Runnable 接口
Runnable接口源码如下,它只有一个run()办法。
public interface Runnable { public abstract void run();}
示例:
public class ThreadDemo implements Runnable { @Override public void run() { System.out.println("通过实现 Runnable 接口创立线程"); } public static void main(String[] args) { ThreadDemo threadDemo = new ThreadDemo(); Thread thread = new Thread(threadDemo); thread.start(); } }
3.2、继承 Thread 类
示例:
public class ThreadDemo extends Thread { @Override public void run() { System.out.println("通过继承 Thread 类创立线程"); } public static void main(String[] args) { ThreadDemo threadDemo = new ThreadDemo(); Thread thread = new Thread(threadDemo); thread.start(); } }
3.3、通过 Callable、Future
通过实现 Runnable 接口与继承 Thread 类的形式创立的线程是没有返回值的,然而在有些状况下,往往须要通过某个线程计算失去的后果供应另一个线程应用,这个时候采纳Runnable 与 Thread 创立线程并通过自行编写代码实现后果返回的形式会不可避免的呈现许多谬误或性能上的问题。基于此问题,JUC并发包(java.util.concurrent)提供了解决方案,实现 Callable 的 call() 办法(这个相似Runnable 接口),应用 Future 的 get() 办法进行获取。
先来看一下Callable接口:
public interface Callable<V> { V call() throws Exception;}
创立过程为:
1、自定义一个类实现Callable接口,重写call()办法;
2、应用JUC包下的 ExecutorService 生成一个对象,应用 submit() 办法失去 Future 对象;
3、采纳 Future 的 get() 办法获取返回值。
示例:
import java.util.concurrent.*; /** * 计算1+2+...+20的后果,开启三个线程,主线程获取两个子线程计算的后果,一个子线程计算1+...+10,一个子线程计算11+...+20。 */public class ThreadDemo implements Callable { //子线程1,用来计算1+...+10 @Override public Object call() throws Exception { int count = 0; for (int i = 1; i <= 10; i++) count = count + i; return count; } public static void main(String[] args) throws Exception { //生成具备两个线程的线程池 ExecutorService executorService = Executors.newFixedThreadPool(2); //调用 executorService.submit() 办法获取 Future 对象 Future<Integer> result1 = executorService.submit(new ThreadDemo()); Future<Integer> result2 = executorService.submit(new SubThread()); //应用 Future的get() 办法期待子线程计算实现返回的后果 int result = result1.get() + result2.get(); //敞开线程池 executorService.shutdown(); System.out.println(result); } } class SubThread implements Callable { //子线程2,用来计算11+...+20 @Override public Object call() throws Exception { int count = 0; for (int i = 11; i <= 20; i++) count = count + i; return count; } }
3.4、通过 JUC 外面的线程池
Executors 的工厂办法提供了 5 种不同的线程池,具体能够看JDK API,如下图。
其实在3.3、通过 Callable、Future 形式创立线程的示例就能看到通过Executors.newFixedThreadPool(2) 工厂办法构建线程池。(Executors 的五种工厂办法的区别及优缺点这里就不说了,下章再细说)
4、几个常见的线程面试题
4.1、线程与过程的区别
过程是操作系统进行资源分配的单元,线程是CPU调度运行的单位;一个过程中能够蕴含很多线程,线程共享过程的内存等资源;每个过程领有各自独立的一套变量,互相不影响,而线程则共享数据,会存在线程平安问题。
4.2、Thread 的 sleep() 办法和 wait() 办法有什么区别和共同点?
相同点:
- 两者都能够暂停线程的执行,都会让线程进入期待状态。
不同点:
- sleep() 办法没有开释锁,而 wait() 办法开释了锁。
- sleep() 办法属于 Thread 类的静态方法,作用于以后线程;而 wait() 办法是 Object 类的实例办法,作用于对象自身。
- 执行 sleep() 办法后,能够通过超时或者调用 interrupt() 办法唤醒休眠中的线程;执行 wait() 办法后,通过调用 notify() 或 notifyAll() 办法唤醒期待线程。
4.3、为什么启动线程时是调用 start() 办法而不是间接调用 run() 办法?
new 一个 Thread 时,线程会进入初始状态;调用 start() 办法时,会启动一个线程并使线程进入就绪状态,当调配到工夫片后就能够开始运行了。 start() 会执行线程的相应筹备工作,而后主动执行 run() 办法的内容,这是真正的多线程工作。 而间接执行 run() 办法,会把 run() 办法当成一个 main 线程下的一般办法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。
一句话总结就是调用 start() 办法可启动线程并使线程进入就绪状态,而 run() 办法只是 Thread 的一个一般办法调用,还是在主线程里执行。
5、总结
通过本文心愿能对你 Java 线程相干的常识把握和面试能有所帮忙,下章持续介绍线程池的相干常识。文中有谬误的中央,还请留言给予斧正,谢谢。喜爱文章的小伙伴们能够关注点赞珍藏哦~
你要的Java材料都在这了,关注公众号即可无套路收费支付!