1、方式一:继承 Thread 类,重写run()方法,调用start()开启线程
public class Thread01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行" + i);
}
}
// main() 是主线程
public static void main(String[] args) throws InterruptedException {
// 创建一个线程对象
Thread01 thread = new Thread01();
// 开启线程
thread.start();
// 主线程
for (int i = 0; i < 500; i++) {
System.out.println(Thread.currentThread().getName() + "正在执行" + i);
}
}
}
结果说明:开启的线程会在主线程中穿插输出结果,说明两个线程同时执行,先后顺序由CPU决定并调度
2、利用多线程下载网上图片案例
- 引入 commons-io 依赖(我这里创建的是maven项目)
<dependencies>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
- 编写代码
public class ThreadDemo01 extends Thread{
// 文件url
private String url;
// 文件名
private String name;
public ThreadDemo01(String url,String name){
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownloader downloader = new WebDownloader();
downloader.downloader(url,name);
System.out.println("下载了图片" + name);
}
public static void main(String[] args) {
String url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595174406474&di=0bf83db78f86413f1016578681ff4366&imgtype=0&src=http%3A%2F%2Fimg.pconline.com.cn%2Fimages%2Fupload%2Fupc%2Ftx%2Fwallpaper%2F1304%2F17%2Fc5%2F19955421_1366189671581.jpg";
String name = "1.jpg";
String url1 = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595174547720&di=7ea8b3e68932fd1ea67733862e95b5f9&imgtype=0&src=http%3A%2F%2Fattachments.gfan.com%2Fforum%2F201604%2F23%2F002205xqdkj84gnw4oi85v.jpg";
String name1 = "2.jpg";
String url2 = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595174659187&di=28310821d7f0b02f0a063fbdc8a8a777&imgtype=0&src=http%3A%2F%2Fimg.pconline.com.cn%2Fimages%2Fupload%2Fupc%2Ftx%2Fwallpaper%2F1305%2F03%2Fc0%2F20496484_1367549048818.jpg";
String name2 = "3.jpg";
ThreadDemo01 t1 = new ThreadDemo01(url, name);
ThreadDemo01 t2 = new ThreadDemo01(url1, name1);
ThreadDemo01 t3 = new ThreadDemo01(url2, name2);
t1.start();
t2.start();
t3.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("下载文件出错 downloader()");
}
}
}
3、方式二:实现 Runnable 接口,重写run()方法,执行线程需要将 Runnable 接口实现类丢入Thread 对象中,调用start()开启线程
public class Thread02 implements Runnable{
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
public static void main(String[] args) {
// 创建 Runnable 实现类对象
Thread02 runnable = new Thread02();
// 创建thread对象,并将runnable实现类对象丢入构造器中
Thread thread = new Thread(runnable);
// 开启线程
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
4、小结
继承 Thread 类
- 子类继承 Thread 类,具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免oop单继承的局限性
实现 Runnable 接口
- 实现接口 Runnable ,具备多线程能力
- 启动线程:Thread 对象构造器中传入 Runnable 实现类对象,Thread 对象.start()
- 推荐使用:避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用
5、利用多线程操作同一资源案例(初始并发问题)
public class ThreadDemo02 implements Runnable{
private int tickets = 10;
public void run() {
while (true){
if (tickets <= 0){
break;
}
// 模拟耗时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets-- + "张票!");
}
}
public static void main(String[] args) {
ThreadDemo02 ticket = new ThreadDemo02();
new Thread(ticket,"张三").start();
new Thread(ticket,"李四").start();
new Thread(ticket,"王五").start();
}
}
输出结果:出现并发问题!
李四正在出售第10张票!
张三正在出售第8张票!
王五正在出售第9张票!
张三正在出售第7张票!
李四正在出售第6张票!
王五正在出售第7张票!
王五正在出售第5张票!
李四正在出售第4张票!
张三正在出售第3张票!
李四正在出售第1张票!
王五正在出售第0张票!
张三正在出售第2张票!
6、龟兔赛跑案例
public class Race implements Runnable{
private static String winner;
public void run() {
for (int i = 0; i <= 100; i++) {
// 模拟兔子休息
if (Thread.currentThread().getName().equals("兔子") && i%20==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 比赛结束标志
boolean flag = gameOver(i);
if (flag){
break;
}
System.out.println(Thread.currentThread().getName() + "-->跑了第" + i + "步");
}
}
// 模拟比赛结束
public boolean gameOver(int step){
if (winner != null){
return true;
}
if (step == 100){
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
7、方式三:实现 Callable<> 接口,重写 call() 方法,利用线程池工具类创建线程池,调用submit()传入Callable对象开启线程,可以获取返回值
public class Thread03 implements Callable<Boolean> {
// 文件url
private String url;
// 文件名
private String name;
public Thread03(String url, String name){
this.url = url;
this.name = name;
}
public Boolean call() throws Exception {
WebDownloader2 downloader = new WebDownloader2();
downloader.downloader(url,name);
System.out.println("下载了图片" + name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
String url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595174406474&di=0bf83db78f86413f1016578681ff4366&imgtype=0&src=http%3A%2F%2Fimg.pconline.com.cn%2Fimages%2Fupload%2Fupc%2Ftx%2Fwallpaper%2F1304%2F17%2Fc5%2F19955421_1366189671581.jpg";
String name = "1.jpg";
String url1 = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595174547720&di=7ea8b3e68932fd1ea67733862e95b5f9&imgtype=0&src=http%3A%2F%2Fattachments.gfan.com%2Fforum%2F201604%2F23%2F002205xqdkj84gnw4oi85v.jpg";
String name1 = "2.jpg";
String url2 = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595174659187&di=28310821d7f0b02f0a063fbdc8a8a777&imgtype=0&src=http%3A%2F%2Fimg.pconline.com.cn%2Fimages%2Fupload%2Fupc%2Ftx%2Fwallpaper%2F1305%2F03%2Fc0%2F20496484_1367549048818.jpg";
String name2 = "3.jpg";
Thread03 t1 = new Thread03(url,name);
Thread03 t2 = new Thread03(url1,name1);
Thread03 t3 = new Thread03(url2,name2);
ExecutorService pool = Executors.newFixedThreadPool(1);
Future<Boolean> r1 = pool.submit(t1);
Future<Boolean> r2 = pool.submit(t2);
Future<Boolean> r3 = pool.submit(t3);
boolean o1 = r1.get();
boolean o2 = r2.get();
boolean o3 = r3.get();
System.out.println(o1);
System.out.println(o2);
System.out.println(o3);
// 关闭线程池
pool.shutdown();
}
}
// 下载器
class WebDownloader2{
// 下载方法
public void downloader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("下载文件出错 downloader()");
}
}
}