起因
前两天,做阅文集团的笔试题发现多线程的问题挺多的,所以再总结一下多线程的问题。
概念
首先区分一下易混淆的概念:
并发与并行
- 并发:事件A和事件B在同一时间段内执行。
- 并行:事件A和事件B在同一时间点执行。
进程与线程
- 进程:正在进行的应用程序,程序的一次执行过程,是系统运行程序的基本单位。(我们可以用任务管理器查看)
- 线程:进程内的一个独立执行的单元(dos框内输入Jconsole可以查看线程的详细情况)
- 多线程:多个线程并发执行
线程的创建方式
线程的创建方式一共有四种:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 线程池
创建实例
1.继承Thread类
创建步骤
1.新建线程类MyThread继承Thread类
2.重写run方法
3.创建测试类,新建线程实例,打印线程信息并与主线程比较
package concurrent;
import java.util.Date;
public class MyThread extends Thread{
public void run() {
for(int i=0;i<10;i++) {
System.out.println(Thread.currentThread().getName()+
"执行时间"+new Date().getTime()+"执行次数"+i);
}
}
}
我们通过继承Thread,重写run方法来实现多线程。
写一个测试类,来看一下效果
package concurrent;
import java.util.Date;
public class Test1 {
public static void main(String[] args) {
//自定义线程
MyThread myThread = new MyThread();
//设置线程名称
myThread.setName("MyThread");
//开启线程
myThread.start();
for(int i=0;i<10;i++) {//打印主线程执行信息
System.out.println(Thread.currentThread().getName()+
"执行时间"+new Date().getTime()+"执行次数"+i);
}
}
}
执行效果
2.实现Runnable接口
创建步骤
1.新建线程类MyRunnable实现Runnable接口
2.重写run方法
3.创建测试类,新建线程实例,打印线程信息并与主线程比较
package concurrent;
import java.util.Date;
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {//打印当前线程的名字,执行时间以及执行次数
System.out.println(Thread.currentThread().getName()+"执行时间"+
new Date().getTime()+"执行次数"+i);
}
}
}
实现Runnable接口中的方法来实现线程。
测试类:通过利用Thread类传入MyRunnable接口来实现线程。
package concurrent;
import java.util.Date;
public class Test2 {
public static void main(String[] args) {
//通过传入Runnable接口和名字自定义线程
Thread thread = new Thread(new MyRunnable(),"MyRunnable");
//开启线程
thread.start();
for(int i=0;i<10;i++) {//打印主线程执行信息
System.out.println(Thread.currentThread().getName()+
"执行时间"+new Date().getTime()+"执行次数"+i);
}
}
}
实现效果:
小结:通过继承Thread类和实现Runnable接口,都是通过实现run方法来创建线程的。
3.实现Callable接口
创建步骤
1.创建MyCallable类实现Callable接口(此处需要定义一个泛型数据类型来表示返回值类型)
2.重写call方法
3.测试类的编写,创建FutureTask实例FutureTask<String> task = new FutureTask<>(new MyCallable());
4.通过Thread类实现 Thread t = new Thread(task)
注意:task.get()可获得call方法返回信息
package concurrent;
import java.util.Date;
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String>{
@Override
public String call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"执行时间"+
new Date().getTime()+"执行次数"+i);
}
return "MyCallable接口实现完成";
}
}
通过实现Callable接口,Callable接口相比较与Runnable接口来说,增加了一个泛型的返回值,并且实现方法变成了call方法,可抛出异常。
测试类:用Callable接口实现线程需要通过创建FutueTask实例来实现。
package concurrent;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class