1.线程是什么?
线程,有时被称为轻量进程(Lightweight Process,LWP),是程序执行流的
最小单元
。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体
,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源
,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享
进程所拥有的全部资源
。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行
。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
2.线程创建的四种方式
- 继承Thread类创建线程,重写run方法
通过观察Thread类源码,发现Thread实际上是实现了Runnable接口的一个实例,即代表一个线程的实例。
class Thread implements Runnable
启动线程:调用start()方法即可;
start()方法与run()的区别在于:start()(开辟一个新的线程)运行在子线程中;run()运行在原线程;
案例:
class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("开启线程方式一");
}
}
使用:
MyThread thread1 = new MyThread();
thread1.start();
- 实现Runnable接口,重写run方法。通过 public Thread(Runnable target)构造方法创建线程
当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
案例:
class MyThread2 extends Student implements Runnable {
@Override
public void run() {
System.out.println("开启线程方式二");
}
}
使用:
MyThread2 thread2 = new MyThread2();
Thread thread = new Thread(thread2);
thread.start();
- 实现Callable接口通过FutureTask包装器实现创建线程
此方法的一般步骤为:
- 自定义类实现Callable< Object>接口,实现call()方法
- 实例化自定义类
- 使用此实例创建FutureTask包装器
- 通过该包装器实例创建线程
案例:
class ThreadCallable<Object> extends Student implements Callable<Object>{
@Override
public Object call() throws Exception {
System.out.println("实现Callable接口通过FutureTask包装器实现的线程");
return null;
}
}
使用:
//方式三
//1.创建实现Callable<V>接口的类
//2.实例化此类对象
//3.通过该callable实例化FutureTask包装器
//4.通过该包装器的实例创建线程
Callable<Object> callable = new ThreadCallable<>();
FutureTask<Object> futureTask = new FutureTask<>(callable);
Thread thread3 = new Thread(futureTask);
thread3.start();
- 通过线程池创建线程
- 通过Executors的静态方法创建线程池
- 通过线程池执行submit等方法即可获取Future对象,通过它.get()方法即获取返回值;get为线程阻塞
案例:
package com.wdl.kt.java.thread;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
/**
* @program: kt1
* @description: 开启线程的四种方式
* @author: wdl
* @create: 2018-11-23 20:46
*/
public class Main {
public static void main(String[] args) {
// //方式1
// MyThread thread1 = new MyThread();
// thread1.start();
//
// //方式2
// MyThread2 thread2 = new MyThread2();
// Thread thread = new Thread(thread2);
// thread.start();
//方式三
//1.创建实现Callable<V>接口的类
//2.实例化此类对象
//3.通过该callable实例化FutureTask包装器
//4.通过该包装器的实例创建线程
// Callable<Object> callable = new ThreadCallable<>();
// FutureTask<Object> futureTask = new FutureTask<>(callable);
// Thread thread3 = new Thread(futureTask);
// thread3.start();
//方式4
//通过线程池
//1.创建一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future> arrayList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
//RunnableThread runnableThread = new RunnableThread();
//executorService.execute(runnableThread);
//executorService.submit(runnableThread);
Callable<String> callable = new ThreadCallable<>(""+i);
Future future = executorService.submit(callable);
arrayList.add(future);
}
for (Future future : arrayList) {
try {
System.out.println(future.get().toString());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
executorService.shutdown();
}
}
class Student {
}
//class MyThread extends Thread {
// @Override
// public void run() {
// super.run();
// System.out.println("开启线程方式一");
// }
//}
//class MyThread2 extends Student implements Runnable {
//
//
// @Override
// public void run() {
// System.out.println("开启线程方式二");
// }
//}
class ThreadCallable<Object> extends Student implements Callable<Object>{
private String taskNumber;
public ThreadCallable(String taskNumber) {
this.taskNumber = taskNumber;
}
@Override
public Object call() throws Exception {
Date date = new Date();
long x = date.getTime();
Thread.sleep(1000);
Date date1 = new Date();
long y = date1.getTime();
//System.out.println("实现Callable接口通过FutureTask包装器实现的线程");
String s = taskNumber+" 运行时间为:"+(y-x);
return (Object) s;
}
}
//class RunnableThread implements Runnable{
// @Override
// public void run() {
// System.out.println("通过线程池创建的线程:"+Thread.currentThread().getName()+" ");
// }
//}
其中ExecutorService提供了
submit
方法,传递一个Callable或者Runnable
,获取一个Future
对象。如果Executor后台线程池还没有完成Callable的计算,调用返回Future对象的get()方法,会阻塞
直到计算完成。
以上就是创建线程的四种方法。
3.上述四种方式可大体分为2类:
- 前两种:无返回值的线程
- 后两种:有返回值的线程
- 其中Callable与Runnable两个接口的区别:
①Callable下的方法是call(),Runnable下的方法是run()①Callable下的方法是call(),Runnable下的方法是run()
②Callable可得到返回值,可抛出异常;Runnable都不可以
③运行Callable任务可得到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成以及检索计算结果;通过Future对象可以了解任务执行的情况,取消任务的执行,获取任务执行的结果
④Thread只支持Runnable接口不支持Callable接口–因此才引入FutureTask
Future接口:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
4.其中Executors
类提供了以下静态方法用来创建线程池,返回的线程池都实现了ExecutorService接口:
public static ExecutorService newFixedThreadPool(int nThreads)
创建固定数目的线程池public static ExecutorService newCachedThreadPool()
创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。public static ExecutorService newSingleThreadExecutor()
创建一个单线程的线程池。public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个可执行周期性动作或者定时任务的线程池。