第一种方法:继承Thread
public class MyThread extends Thread{
public static void main(String[] args) {
/**
* 1 继承Thread类 重写run(),Thread.start()
*/
MyThread t = new MyThread();
t.start();
}
@Override
public void run() {
System.out.println("启动线程");
}
}
第二种方法:实现Runnable
public class MyThread{
public static void main(String[] args) {
showTimeTask task = new showTimeTask();
Thread t = new Thread(task);
t.start();
}
}
//实现Runnable接口
class showTimeTask implements Runnable{
boolean flag = true;
@Override
public void run() {
while(flag){
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("线程2: showTimeTask当前时间:"+sdf.format(d));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
匿名类(也算是第二种方法实现Runnable)
public class MyThread{
public static void main(String[] args) {
Runnable r = new Runnable() {
//Runnable 是接口
boolean flag = true;
@Override
public void run() {
while(flag){
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("线程当前时间:"+sdf.format(d));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new Thread(r);
t2.start();
}
}
---------------------------------使用Callable和Future创建线程------------------------------
第三种方法:实现Callable接口
实现Callable与Runnable不同,Callable接口提供的是一个call()方法作为线程执行体,call()比run()更强大:
1)call()有返回值
2)call()可以声明抛出异常
public class MyThread{
public static void main(String[] args) {
try {
Test t = new MyThread().new Test();
t.call();
} catch (Exception e) {
e.printStackTrace();
}
}
class Test implements Callable{
@Override
public Object call() throws Exception {
System.out.println("开启线程");
return null;
}
}
}
第四种方法:实现Futrue接口
Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。
>boolean cancel(boolean mayInterruptIfRunning):视图取消该Future里面关联的Callable任务
>V get():返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值
>V get(long timeout,TimeUnit unit):返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException
>boolean isDone():若Callable任务完成,返回True
>boolean isCancelled():如果在Callable任务正常完成前被取消,返回True
介绍了相关的概念之后,创建并启动有返回值的线程的步骤如下:
public class MyThread{
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<Double> task = new FutureTask(new MyCallable());
//创建一个线程,异步计算结果
Thread thread = new Thread(task);
thread.start();
//主线程继续工作
Thread.sleep(1000);
System.out.println("主线程等待计算结果...");
//当需要用到异步计算的结果时,阻塞获取这个结果
Double d = task.get();
System.out.println("计算结果是:"+d);
//用同一个 FutureTask 再起一个线程
Thread thread2 = new Thread(task);
thread2.start();
}
}
class MyCallable implements Callable<Double>{
@Override
public Double call() {
double d = 0;
try {
System.out.println("异步计算开始.......");
d = Math.random()*10;
d += 1000;
Thread.sleep(2000);
System.out.println("异步计算结束.......");
} catch (InterruptedException e) {
e.printStackTrace();
}
return d;
}
}
第五种方法:使用线程池ExecutorSerice、Executors
前面三种方法,都是显式地创建一个线程,可以直接控制线程,如线程的优先级、线程是否是守护线程,线程何时启动等等。而第四种方法,则是创建一个线程池,池中可以有1个或多个线程,这些线程都是线程池去维护,控制程序员不需要关心这些细节,只需要将任务提交给线程池去处理便可,非常方便。
创建线程池的前提最好是你的任务量大,因为创建线程池的开销比创建一个线程大得多。
创建线程池的方式
ExecutorService
是一个比较重要的接口,实现这个接口的子类有两个 ThreadPoolExecutor (普通线程池)、ScheduleThreadPoolExecutor (定时任务的线程池)。你可以通过这两个类来创建一个线程池,但要传入各种参数,不太方便。
为了方便用户,JDK中提供了工具类Executors
,提供了几个创建常用的线程池的工厂方法。
public class MyThread{
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建一个只有一个线程的线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
//创建任务,并提交任务到线程池中
executorService.execute(new MyRunable("任务1"));
executorService.execute(new MyRunable("任务2"));
executorService.execute(new MyRunable("任务3"));
}
}
class MyRunable implements Runnable{
private String taskName;
public MyRunable(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
System.out.println("线程池完成任务:"+taskName);
}
}