第一种方法:继承Thread类,重写run方法

 

import java.util.concurrent.CountDownLatch;

public class MyThread extends Thread{


@Override
public void run() {
long start = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"开始运行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行结束");
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"运行耗时:"+(end-start)+"毫秒");
}

}

 

 

public class ThreadMain {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始运行");
long start = System.currentTimeMillis();
MyThread thread1 = new MyThread();
thread1.setName("thread1");
MyThread thread2 = new MyThread();
thread2.setName("thread2");
MyThread thread3 = new MyThread();
thread3.setName("thread3");
MyThread thread4 = new MyThread();
thread4.setName("thread4");

thread1.start();
/*thread2.start();
thread3.start();
thread4.start();
*/
thread1.join();
long end = System.currentTimeMillis();
System.out.println("主现场运行结束,耗时"+(end-start)+"毫秒");
}
}

 

运行结果:

主线程开始运行
主现场运行结束,耗时1毫秒
Thread-1开始运行
thread4开始运行
Thread-3开始运行
Thread-2开始运行
Thread-1运行结束
Thread-1运行耗时:1001毫秒
thread4运行结束
Thread-2运行结束
Thread-3运行结束
Thread-2运行耗时:1001毫秒
thread4运行耗时:1002毫秒
Thread-3运行耗时:1001毫秒

 

思考:子线程还没有结束,主线程先结束了,怎么让主线程在子线程之后结束?

1、在让子线程join进主线程

2、使用Futrue得到线程的返回值,得到返回值主线程才会停止,否则futrue.get()方法会一直阻塞主线程

以上两个方法适合主线程中有一两个子线程,如果子线程多的话,就石乐志了

3、使用CountDownLatch(倒数计数器),创建一个CountDownLatch实例,根据子线程数量初始化倒计数,在子线程中使用CountDownLatch.countDown()方法,使计数-1,在主线程中使用CountDownLatch.await()方法,等计数为零时,主线程继续执行

 

第二种方法:实现Runnable接口

 

 

public class MyRunnable implements Runnable{

@Override
public void run() {
long start = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"开始运行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行结束");
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"运行耗时:"+(end-start)+"毫秒");
}

}

 

 

public class MyRunnableMain {
public static void main(String[] args) {
System.out.println("主线程开始");
long start = System.currentTimeMillis();
Runnable r1 = new MyRunnable();
Thread thread1 = new Thread(r1);
Thread thread2 = new Thread(r1);
Thread thread3 = new Thread(r1);
Thread thread4 = new Thread(r1);
Thread thread5 = new Thread(r1);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
long end = System.currentTimeMillis();
System.out.println("主线程结束,耗时:"+(end-start)+"毫秒");
}
}

 

运行结果:

主线程开始
主线程结束,耗时:1毫秒
Thread-0开始运行
Thread-1开始运行
Thread-2开始运行
Thread-3开始运行
Thread-4开始运行
Thread-1运行结束
Thread-0运行结束
Thread-1运行耗时:1000毫秒
Thread-0运行耗时:1000毫秒
Thread-2运行结束
Thread-3运行结束
Thread-3运行耗时:1001毫秒
Thread-2运行耗时:1002毫秒
Thread-4运行结束
Thread-4运行耗时:1001毫秒

 

思考:thread和runnable两种方式哪种更好?为什么?

runnable方式创建线程更好

使用runnable接口的方式能避免java单继承带来的缺陷

使用runnable接口能够被多个线程共享,适用于多个线程共享资源

 

第三种方式:Callable接口,带有Future返回值的线程


import java.util.concurrent.Callable;

public class MyCallable implements Callable<String>{

private String name;

public MyCallable(String name) {
this.name = name;
}

 

@Override
public String call() throws Exception {
long start = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"开始运行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行结束");
long end = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"运行耗时:"+(end-start)+"毫秒");
return "这是"+name+"线程返回的结果";
}

}

 

public class MyCallableMain {
public static void main(String[] args) {
System.out.println("主程序开始运行");
// 记录起始时间
long start = System.currentTimeMillis();
// 使用list记录Future结果集
List<Future> list = new ArrayList<Future>();
final int POOL_SIZE = 5;
// 创建线程池
ExecutorService pools = Executors.newFixedThreadPool(POOL_SIZE);
for(int i=0; i<POOL_SIZE; i++){
Callable c = new MyCallable("callable"+(i+1));
// 获取线程的返回值
Future future = pools.submit(c);
list.add(future);
}
// 线程池必须关闭,否则主程序无法结束
pools.shutdown();
for(Future future : list){
try {
System.out.println(future.get().toString());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println("主程序运行结束,耗时:"+(end-start)+"毫秒");
}
}

 

运行结果:

主程序开始运行
pool-1-thread-2开始运行
pool-1-thread-1开始运行
pool-1-thread-4开始运行
pool-1-thread-5开始运行
pool-1-thread-3开始运行
pool-1-thread-2运行结束
pool-1-thread-1运行结束
pool-1-thread-2运行耗时:1001毫秒
pool-1-thread-1运行耗时:1001毫秒
这是callable1线程返回的结果
这是callable2线程返回的结果
pool-1-thread-5运行结束
pool-1-thread-4运行结束
pool-1-thread-4运行耗时:1002毫秒
pool-1-thread-5运行耗时:1001毫秒
pool-1-thread-3运行结束
pool-1-thread-3运行耗时:1002毫秒
这是callable3线程返回的结果
这是callable4线程返回的结果
这是callable5线程返回的结果
主程序运行结束,耗时:1007毫秒

 

补充:使用callable创建线程的好处是:它可以带有返回值,配合Future接口来接受callable的返回值

接受返回值的方式有两种:

第一种:使用线程池来执行线程,submit()方法返回future对象

第二种:使用Future接口的FutureTask实现类来接受callable的实现类,FutureTask类还实现了Runnable接口,因此可以作为Thread类的target入参