1.继承Thread类
第一步,创建一个线程类并继承Thread类
第二步,重写run()方法,内部自定义线程执行体
第三步,创建自定义的线程类对象,调用start()方法,启动线程
示例代码如下
public class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 5; i++){
System.out.println("自定义线程"+i);
}
}
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
myThread1.start();
for (int i = 0; i < 10; i++){
System.out.println("主线程"+i);
}
}
}
ps:为什么不直接调用run()方法而是选择start()方法呢?
调用run()方法
调用start()方法
由此可见
如果在main方法中调用run()方法,只有主线程一条执行路径,如果调用start()方法,子线程的执行路径就会与主线程分开,两者将会并行且交替执行
2.实现Runnable接口
相比于第一种方式,第二种的前两步基本与第一种相同,主要的区别在于第三步
第一步,创建一个线程类并实现Runnable接口
第二步,实现run()方法,内部自定义线程执行体
第三步,创建实现类的对象并将其作为参数放入Thread的构造方法中创建线程对象,调用start()方法,启动线程
代码示例如下
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++){
System.out.println("自定义线程"+i);
}
}
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread myThread = new Thread(myRunnable);
myThread.start();
for (int i = 0; i < 10; i++){
System.out.println("主线程"+i);
}
}
}
ps:此处用到了代理的思想,我们自定义的MyRunnable是没有start()方法的,因此我们选择让Thread类去帮助我们实现,myThread即代理对象
3.实现Callable接口
相比上面两种创建线程的方式,通过Callable来实现的方式拥有了异常处理和返回值的能力
第一步,创建一个类并实现Callable接口,需要在此设置返回值的类型
第二步,重写call()方法,需要抛出异常
第三步,创建目标对象,MyCallable,这里我给了他一个构造方法,方便我们定位到对象
第四步,创建一个线程池,用来执行我们创建的线程,这里的3代表创建的线程池的一个容量
ps:newFixedThreadPool 为定长线程池,这里的3就是他的最大并发数,若线程数超出这个数量则需要在队列中等待。线程池有几种类型此处不过多赘述。
第五步,将我们创建好的线程对象通过线程池对象的submit()方法提交,在此之后线程池会安排我们的线程去执行,并将结果返回,我们使用Future创建对象进行接收。
第六步,我们可以通过Future对象的get()方法获取到每个线程的执行结果
第七步,使用线程池的shutdownNow()方法关闭我们的线程池服务
ps:一定不要忘记关闭服务
示例代码如下
public class MyCallable implements Callable<String> {
int ID;//标记区分不同的线程对象
//写一个构造方法
public MyCallable(int ID) {
this.ID = ID;
}
@Override
public String call() throws Exception {
//模拟线程处理任务
Thread.sleep(1000);
System.out.println("睡醒了-->"+ID);
return "自定义线程执行完毕-->"+ID;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程对象1,2,3
MyCallable myCallable1 = new MyCallable(1);
MyCallable myCallable2 = new MyCallable(2);
MyCallable myCallable3 = new MyCallable(3);
//创建线程池对象,提供线程池服务
ExecutorService service = Executors.newFixedThreadPool(3);
//通过线程池对象提交我们创建好的线程对象,并且声明Future对象来接收返回值
Future<String> future1 = service.submit(myCallable1);
Future<String> future2 = service.submit(myCallable2);
Future<String> future3 = service.submit(myCallable3);
//输出返回值
System.out.println(future1.get());
System.out.println(future2.get());
System.out.println(future3.get());
//关闭线程池服务
service.shutdownNow();
}
}
创建线程的方法五花八门,我们只要掌握最基本的方法就可以,每个人都有自己的编码习惯,也有自己惯用的线程创建方法,但是都是基于我们所说的这三种方式去实现的。
关于那种方式最好,我更推荐大家用Runnable或者Callable,第一种方式了解就行,至于Runnable和Callable如何选择,则需要根据项目所需,按需取舍。
ps:如果有写的不对的地方,欢迎各位大佬反馈,谢谢!