某一天你在面试时遇到了线程的相关问题。
面试官:“你知道有哪几种建立线程的方式吗?”
(此时你的心理活动:哈哈小意思这能难住我,忍住激动伪装淡定道)
你:“嗯,能够经过实现 Runnable 接口和继承 Thread 类来建立线程。”
面试官:“除了这两种还有其余方式吗?”
你:“emmm...还有吗?”
面试官:“知道经过实现 Callable 接口与获取 Future 对象来实现吗?”
你:“emmm不知道...不过如今知道了嘻嘻”
面试官:“那建立线程池有哪些方式呢?”
你:“能够经过 ThreadPoolExecutor 构造函数或者 Executors 提供的工厂方法来建立”
面试官:“那经过不一样的 Executors 工厂方法建立线程池之间有什么区别呢?”
你:“emmm...“
面试官:“那 ThreadPoolExecutor 构造函数中的工做队列和拒绝策略分别有哪些呢?”
你:“emmm...“
(此时你的心理活动:QAQ...不面了,把劳资简历还我!) java
概念:线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程本身不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的所有资源。一个线程能够建立和撤消另外一个线程,同一进程中的多个线程之间能够并发执行。面试
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:终止状态,表示该线程已经执行完。多线程
注意下图状态之间的切换。架构
Runnable接口源码以下,它只有一个run()方法。并发
public interface Runnable { public abstract void run(); }
示例:ide
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(); } }
示例:函数
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(); } }
经过实现 Runnable 接口与继承 Thread 类的方式建立的线程是没有返回值的,然而在有些状况下,每每须要经过某个线程计算获得的结果供给另外一个线程使用,这个时候采用Runnable 与 Thread 建立线程并经过自行编写代码实现结果返回的方式会不可避免的出现许多错误或性能上的问题。基于此问题,JUC并发包(java.util.concurrent)提供了解决方案,实现 Callable 的 call() 方法(这个相似Runnable 接口),使用 Future 的 get() 方法进行获取。
先来看一下Callable接口:性能
public interface Callable<V> { V call() throws Exception; }
建立过程为:
一、自定义一个类实现Callable接口,重写call()方法;
二、使用JUC包下的 ExecutorService 生成一个对象,使用 submit() 方法获得 Future 对象;
三、采用 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; } }
Executors 的工厂方法提供了 5 种不一样的线程池,具体能够看JDK API,以下图。
其实在3.三、经过 Callable、Future 方式建立线程的示例就能看到经过Executors.newFixedThreadPool(2) 工厂方法构建线程池。(Executors 的五种工厂方法的区别及优缺点这里就不说了,下章再细说)
进程是操做系统进行资源分配的单元,线程是CPU调度运行的单位;一个进程中能够包含不少线程,线程共享进程的内存等资源;每一个进程拥有各自独立的一套变量,相互不影响,而线程则共享数据,会存在线程安全问题。
相同点:
不一样点:
new 一个 Thread 时,线程会进入初始状态;调用 start() 方法时,会启动一个线程并使线程进入就绪状态,当分配到时间片后就能够开始运行了。 start() 会执行线程的相应准备工做,而后自动执行 run() 方法的内容,这是真正的多线程工做。 而直接执行 run() 方法,会把 run() 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,因此这并非多线程工做。
一句话总结就是调用 start() 方法可启动线程并使线程进入就绪状态,而 run() 方法只是 Thread 的一个普通方法调用,仍是在主线程里执行。
经过本文但愿能对你 Java 线程相关的知识掌握和面试能有所帮助,下章继续介绍线程池的相关知识。文中有错误的地方,还请留言给予指正,谢谢。喜欢文章的小伙伴们能够关注点赞收藏哦~
也欢迎你们关注个人公众号:Java的成神之路,有最新面试资料,必看电子书,架构进阶相关资料等。