一. 简介
1.1 线程与进程的概念
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程。(进程是资源分配的最小单位)
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。(线程是cpu调度的最小单位)
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。采用多线程的编程方式,能充分利用 CPU 资源,显著的提升程序的执行效率。
二. 多线程的实现
2.1. 继承thread
通过继承Thread类来创建线程是最简单的一种方法,继承类重写run()方法,然后通过线程对象实例去调用start()方法即可启动线程。
public class ThreadTest extends Thread {
private String msg;
public ThreadTest (String msg) {
this.msg = msg;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(msg + "运行 : " + i);
try {
sleep((int) (Math.random() * 5));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadTest t1 = new ThreadTest("zhangsan");
ThreadTest t2 = new ThreadTest("sunny");
t1.start();
t2.start();
}
}
运行结果为:
zhangsan运行 : 0
sunny运行 : 0
sunny运行 : 1
sunny运行 : 2
zhangsan运行 : 1
zhangsan运行 : 2
zhangsan运行 : 3
sunny运行 : 3
zhangsan运行 : 4
sunny运行 : 4
注意:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的;多线程程序是乱序执行。多个线程分别完成自己的任务。
2.2 实现Runnable
多个线程共同完成一个任务;不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的。
开发者只需要实现Runnable接口,然后通过一个Thread类来启动。
public class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "在运行!");
}
}
Thread thread = new Thread(new MyThread());
thread.start();
2.3 实现Callable
使用Callable+Future获取执行结果,并且可以抛出异常。
public class CallableDemo implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
System.out.println("Callable子线程开始计算啦!" + Thread.currentThread().getName());
Thread.sleep(2000);
for(int i=0 ;i<5000;i++){
sum=sum+i;
}
System.out.println("Callable子线程计算结束!" + Thread.currentThread().getName());
return sum;
}
}
Callable执行测试类如下:
public class CallableTest {
public static void main(String[] args) {
//创建线程池
ExecutorService es = Executors.newSingleThreadExecutor();
//创建Callable对象任务
CallableDemo calTask = new CallableDemo();
//提交任务并获取执行结果
Future<Integer> future = es.submit(calTask);
//关闭线程池
es.shutdown();
try {
Thread.sleep(2000);
System.out.println("主线程在执行其他任务");
if (future.get() != null) {
//输出获取到的结果
System.out.println("future.get()-->" + future.get());
} else {
//输出获取到的结果
System.out.println("future.get()未获取到结果");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("主线程在执行完成");
}
}
2.4 使用 JDK 8 的 Lambda 创建线程
Lambda 表达式,是从 JDK1.8 版本开始加入的,可以看作成通过实现Runnable接口创建线程的一种简写。
new Thread(()-> System.out.println(Thread.currentThread().getName() + "在运行!")).start();
2.5 总结
实现Runnable接口比继承Thread类所具有的优势:
- 适合多个相同的程序代码的线程去处理同一个资源
- 可以避免java中的单继承的限制
- 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
- 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类。
需要特别注意的地方是:真正启动线程的是start()方法而不是run()方法,单独调用run()方法和调用普通的成员方法一样,不能启动线程。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。