什么是多线程
Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。
如何使用
继承Thread类创建线程
/**
* 多线程实现的第一种方法,继承Thread
*
*/
//1.自定义一个类,继承java.lang包下的Thread类
class MyThread extends Thread {
//2.重写run方法
public void run() {
//3.将要在线程中执行的代码编写在run方法中
for(int i = 0; i < 1000; i++) {
System.out.println("monkey");
}
}
}
public class ThreadTest01 extends Thread {
public static void main(String[] args) {
//4.创建上面自定义类的对象
//创建一个线程
MyThread mt = new MyThread();
//5.调用start方法启动线程
mt.start();
//将循环的次数设置多一些,否则还没等到CPU切换就已经打印完成了
for(int i = 0; i < 1000; i++) {
System.out.println("1024");
}
}
}
实现Runnable接口创建线程
/**
* 多线程实现的第二种方法,实现Runnable接口
*
*/
// 1.自定义一个类实现java.lang包下的Runnable接口
class MyRunnable implements Runnable {
// 2.重写run方法
@Override
public void run() {
// 3.将要在线程中执行的代码编写在run方法中
for (int i = 0; i < 1000; i++) {
System.out.println("monkey");
}
}
}
public class ThreadTest02 {
public static void main(String[] args) {
// 4.创建上面自定义类的对象
MyRunnable mr = new MyRunnable();
// 5.创建Thread对象并将上面自定义类的对象作为参数传递给Thread的构造方法
Thread t = new Thread(mr);
//6.调用start方法启动线程
t.start();
for (int i = 0; i < 1000; i++) {
System.out.println("1024");
}
}
}
实现Callable接口创建线程
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 多线程实现的第三种方法,实现Callable接口 优点: 可以获取返回值 可以抛出异常
*/
//1.定义一个类实现Callable<V>接口
class MyCallable implements Callable<Integer> {
//睡眠时间
private long second;
//计算阶乘
private int count;
public MyCallable(int count, long second) {
this.count = count;
this.second = second;
}
// 2.重写call方法
@Override
public Integer call() throws Exception {
// 3.将要执行的代码写在call方法中
// 计算阶乘
//让当前线程睡眠,单位是毫秒
Thread.sleep(second);
int sum = 1;
if (count == 0) {
sum = 0;
} else {
for (int i = 1; i <= count; i++) {
sum *= i;
}
}
// 打印线程名称
System.out.println(Thread.currentThread().getName() + "-----sum=" + sum);
return sum;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
public class ThreadTest03 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//4.创建ExecutorService线程池
ExecutorService exec = Executors.newCachedThreadPool();
//5.创建存储Future对象的集合,用来存放ExecutorService的执行结果
ArrayList<Future<Integer>> results = new ArrayList<Future<Integer>>();
//6.开启2个线程,将返回的Future对象放入集合中
for (int i = 0; i < 2; i++) {
if (i == 0) {
//计算5的阶乘,睡眠10秒
results.add(exec.submit(new MyCallable(5, 10000)));
} else {
//计算3的阶乘,睡眠i秒
results.add(exec.submit(new MyCallable(3, i)));
}
}
for (Future<Integer> fs : results) {
//7.判断线程是否执行结束,如果执行结束就将结果打印
if (fs.isDone()) {
System.out.println("计算结果:" + fs.get());
} else {
System.out.println(fs.toString() + "该线程还没有计算完毕,请耐心等待");
}
}
//8.关闭线程池,不再接收新的线程,未执行完的线程不会被关闭
exec.shutdown();
System.out.println("main方法执行结束");
}
}
三种实现方式的比较
- 继承Thread
- 优点:可以直接使用Thread类中的方法,代码简单
- 缺点:继承Thread类之后就不能继承其他的类
- 实现Runnable接口
- 优点:即时自定义类已经有父类了也不受影响,因为可以实现多个接口
- 缺点: 在run方法内部需要获取到当前线程的Thread对象后才能使用Thread中的方法
- 实现Callable接口
- 优点:可以获取返回值,可以抛出异常
- 缺点:代码编写较为复杂