线程通常可以通过三种方式来创建,第一种是继承Thread类,第二中是实现Runnable接口,第三种则是实现Callable接口,其中第二种方式是最重要的,而且Thread类也是实现的Runnable接口。
继承Thread类
首先自定义线程类继承Thread类,然后重写Run方法,在Run方法中编写线程执行体,最后就可以创建线程对象,调用start方法启动线程。先看一个使用多线程下载三张图片的例子:
import org.apache.commons.io.FileUtils;import java.io.File;import java.io.IOException;import java.net.URL;/*** @author wjh* @date 2021/7/15 10:37* @Package PACKAGE_NAME*/public class MyThread extends Thread {private String url;private String name;public MyThread(String url, String name) {this.url = url;this.name = name;}@Overridepublic void run() {WebDownloader webDownloader = new WebDownloader();webDownloader.downloader(url, name);System.out.println("下载了文件名为:" + name);}public static void main(String[] args) {MyThread myThread1 = new MyThread("https://static.runoob.com/images/demo/demo2.jpg", "1.jpg");MyThread myThread2 = new MyThread("http://img.desktx.com/d/file/wallpaper/scenery/20170107/080145c3a7460e7fa0369052a11467db.jpg", "2.jpg");MyThread myThread3 = new MyThread("http://img.ewebweb.com/uploads/20190623/21/1561296521-HiSnYbhyeE.jpg", "3.jpg");myThread1.start();myThread2.start();myThread3.start();}}class WebDownloader {public void downloader(String url, String name) {try {FileUtils.copyURLToFile(new URL(url), new File(name));} catch (IOException e) {e.printStackTrace();System.out.println("IO异常,downloader方法出现问题");}}}
运行结果:
可以看到下载顺序是321而不是按照123来下载,其实这三条线程不是按照代码的编写顺序执行的,而是同时执行的。
实现Runnable接口
首先自定义线程类实现Runnable接口,然后在Run方法中编写线程执行体,然后创建线程对象(Thread),而参数是实现Runnable接口的实例对象,然后使用Thread类的start方法启动线程。
/*** @author wjh* @date 2021/7/15 10:56* @Package PACKAGE_NAME*/public class TestThread implements Runnable{String title;public TestThread(String title){this.title=title;}@Overridepublic void run() {for(int i=0;i<10;i++){System.out.println(title+":"+i);}}public static void main(String[] args) {TestThread testThread1=new TestThread("线程1");TestThread testThread2=new TestThread("线程2");new Thread(testThread1).start();new Thread(testThread2).start();}}

继承Thread类和实现Runnable接口的对比:
继承Thread:子类继承Thread类具有多线程能力,可以通过“子类对象.start()”来启动线程,但是由于Java单继承的机制限制,不建议使用该方法来创建线程。
实现Runnable接口:实现Runnable接口的类也具有多线程能力,可以通过“new Thread(实现接口的实例对象).start()”来启动线程,采用实现接口的方式避免了单继承的局限性,灵活方便,方便同一个对象被多个线程使用,建议使用这种方法来创建线程。
实现Callable接口
步骤:
- 实现Callable接口,需要返回值类型;
- 重写call方法,需要抛出异常;
- 创建目标对象;
- 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行:Future result1 = ser.submit(11);
- 获取结果:boolean r1 = result1.get()
关闭服务:ser.shutdownNow();
/*** 实现Callable接口*/public class Demo6_CreateCallable implements Callable<Boolean> {private String url;//网络图片地址private String name;//报错扥文件名//有参构造public Demo6_CreateCallable(String url, String name) {this.url = url;this.name = name;}//下载图片线程的执行体public Boolean call() throws Exception {WebDownloader webDownloader = new WebDownloader();webDownloader.downloader(url, name);System.out.println("下载了文件名为:" + name);return true;}public static void main(String[] args) throws ExecutionException, InterruptedException {Demo6_CreateCallable c = new Demo6_CreateCallable("https://img-home.csdnimg.cn/images/20201124032511.png", "1.png");Demo6_CreateCallable c1 = new Demo6_CreateCallable("https://img-home.csdnimg.cn/images/20201124032511.png", "2.png");Demo6_CreateCallable c2 = new Demo6_CreateCallable("https://img-home.csdnimg.cn/images/20201124032511.png", "3.png");//创建执行服务ExecutorService ser = Executors.newFixedThreadPool(3);//提交执行Future<Boolean> r = ser.submit(c);Future<Boolean> r1 = ser.submit(c1);Future<Boolean> r2 = ser.submit(c2);//获取结果boolean res = r.get();boolean res1 = r1.get();boolean res2 = r2.get();//关闭服务ser.shutdownNow();}}
