简单来说,并发是并行运行多个程序或程序的几个部分的能力。并发使程序能够通过利用底层操作系统和机器硬件的未开发功能来实现高性能和吞吐量。例如,现代计算机在一个 CPU 中有多个内核,程序可以利用所有内核进行某些处理;因此与顺序处理相比,在时间上更早地完成了任务。
Java并发的支柱是线程。线程是一个轻量级进程,它有自己的调用栈,但可以访问同一进程中其他线程的共享数据。默认情况下,Java 应用程序在一个进程中运行。在 Java 应用程序中,您可以使用多个线程来实现并行处理或并发执行。
线程状态
在 Java 中,线程有 6 种状态:NEW / RUNNABLE / BLOCKED / WAITING / TIMED_WAITING / TEMINATED,这是 JDK 源码中定义的 6 种状态。
但是操作系统一般把线程划分为 5 种状态:新建、就绪、运行、阻塞、死亡。
因此,我们经常看到类似下图的线程状态流转示例。
如何让 Java 并发
要使 java 类并发,首先需要了解的是 java.lang.Thread
类,这个类是java中所有并发概念的基础。其次,也可以用 java.lang.Runnable
接口从线程类中抽象出线程行为。
构建高级应用程序所需的其他类可以在 Java 1.5 中添加的包 java.util.concurrent
中找到。
Thread 类与 Runnable 接口
在 Java 语言中,创建线程有两种方式:一个实现 Runnable 接口,另一个通过继承 Thread 类。
实现Runnable接口:
public class DemoRunnable implements Runnable {
public void run() {
//Code
}
}
新建一个线程的方法:
new Thread(new DemoRunnable()).start()
public class DemoThread extends Thread {
public DemoThread() {
super("DemoThread");
}
public void run() {
//Code
}
}
新建一个线程的方法:
new DemoThread().start()
Runnable 与 Thread 的区别
- 实现 Runnable 是首选的方法。在这里,您并没有真正修改线程的行为。
- Java 只支持单继承,所以只能扩展一个类。
- 实例化接口可以更清晰地分离代码和线程的实现。
- 实现 Runnable 使类更加灵活。如果扩展Thread,那么您正在执行的操作将始终在一个线程中。但是,如果实现 Runnable,则不必如此。你可以在一个线程中运行它,或者将它传递给某种执行器服务,或者只是将它作为一个单线程应用程序中的任务传递。
Java Executor
在 Java 中创建线程是一个非常昂贵的过程,其中还包括内存开销。因此,迫切需要一种机制,可以在创建后,重新使用这些线程来运行我们未来的可运行文件。Executor 为此而诞生了,它自 JDK5 出现,并且在后来的发展中一直在演进,我们将其称之为 线程池。
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快;另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
Java 中提供了多种创建线程池的方式:
- Executors:线程池创建工厂类
- public static ExecutorServicenewFixedThreadPool(int nThreads):返回线程池对象
- ExecutorService:线程池类
- Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
- Future 接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用