三种创建方式
Thread
- 自定义线程类继承 Thread 类
- 重写 run() 方法,编写线程执行体
- 创建线程对象,调用 start() 方法启动线程
package com.moon.demo01;
/*
创建线程方式:
1. 继承 Thread 类
2. 重写 run() 方法
3. 调用 start() 开启线程
线程开启不一定立即执行,由 CPU 调度执行
*/
public class TestThread extends Thread {
// 线程入口点
@Override
public void run() {
// run 方法 线程体
// 线程不一定立即执行,由 CPU 安排调度
for (int i = 0; i < 200; i++) {
System.out.println("多线程--" + i);
}
}
public static void main(String[] args) {
// main 方法 主线程
// 创建线程对象
TestThread testThread = new TestThread();
// 调用 start() 方法开启线程
testThread.start();
for (int i = 0; i < 2000; i++) {
System.out.println("main 方法--" + i);
}
}
}
网络图片下载练习
package com.moon.demo02;
// 需要引入 commons-io-2.8.0.jar 包
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
// 下载器
public 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 方法异常");
}
}
}
package com.moon.demo02;
// 练习 Thread,实现多线程同步下载图片
public class TestThread extends Thread{
private String url; // 网络图片地址
private String name; // 保存的文件名
public TestThread(String url, String name) {
this.url = url;
this.name = name;
}
// 下载图片线程的执行体
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载文件" + name + "成功");
}
public static void main(String[] args) {
TestThread t1 = new TestThread("http://img.netbian.com/file/2021/0310/smallf677a15bc950aef69cec1704ea509cb61615386187.jpg", "01.jpg");
TestThread t2 = new TestThread("http://img.netbian.com/file/2021/0326/smallae961e173d8c70abb0107fcc0d5b195b1616774139.jpg", "02.jpg");
TestThread t3 = new TestThread("http://img.netbian.com/file/2021/0328/smalle261b1a44ca68a9e5be68aeafe7fe83e1616932954.jpg", "03.jpg");
t1.start();
t2.start();
t3.start();
/*
下载文件02.jpg成功
下载文件01.jpg成功
下载文件03.jpg成功
// 并不一定按照预想的程序顺序执行下载,每次下载的先后顺序也不一样,由 CPU 调度执行
*/
}
}
Runnable
- 定义 MyRunnable 类,实现 Runnable 接口
- 实现 run() 方法,编写线程执行体
- 创建线程对象, 调用 start() 方法启动线程
- 推荐使用 Runnable 对象,因为 Java 单继承的局限性
package com.moon.demo03;
/*
创建线程方式
1. 实现 runnable 接口
2. 实现 run 方法
3. 执行线程需要创建线程对象并丢入 runnable 接口实现类,调用 start() 方法
*/
public class TestThread implements Runnable {
@Override
public void run() {
// 实现 run 方法 线程体
for (int i = 0; i < 200; i++) {
System.out.println("多线程--" + i);
}
}
public static void main(String[] args) {
// main 方法 主线程
// 创建 runnable 接口的实现对象
TestThread testThread = new TestThread();
// 创建线程对象,通过线程对象来开启线程 代理
//Thread thread = new Thread(testThread);
// 开启线程
//thread.start();
// 以上两个步骤可简写为
new Thread(testThread).start();
for (int i = 0; i < 2000; i++) {
System.out.println("main 方法--" + i);
}
}
}
以上网络图片下载练习使用 Runnable 实现
package com.moon.demo02;
// 练习 Thread,实现多线程同步下载图片
public class TestThread implements Runnable{
private String url; // 网络图片地址
private String name; // 保存的文件名
public TestThread(String url, String name) {
this.url = url;
this.name = name;
}
// 下载图片线程的执行体
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载文件" + name + "成功");
}
public static void main(String[] args) {
TestThread t1 = new TestThread("http://img.netbian.com/file/2021/0310/smallf677a15bc950aef69cec1704ea509cb61615386187.jpg", "01.jpg");
TestThread t2 = new TestThread("http://img.netbian.com/file/2021/0326/smallae961e173d8c70abb0107fcc0d5b195b1616774139.jpg", "02.jpg");
TestThread t3 = new TestThread("http://img.netbian.com/file/2021/0328/smalle261b1a44ca68a9e5be68aeafe7fe83e1616932954.jpg", "03.jpg");
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}
}
小结:
- 继承 Thread 类
- 子类继承 Thread 类,具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免 OOP 单继承局限性
- 实现 Runnable 接口
- 实现接口 Runnable,具有多线程能力
- 启动线程:传入目标对象 + Thread 对象.start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
package com.moon.demo04;
// 多个线程同时操作一个对象
// 模拟买票例子
// 发现问题:多个线程操作同一个资源的情况下,线程不安全、数据紊乱
public class TestThread implements Runnable {
// 票数
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
// 模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 获取当前执行的线程名
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "--> 抢到了第" + ticketNums-- + "张票");
}
}
public static void main(String[] args) {
TestThread ticket = new TestThread();
// 一个对象被多个线程使用
new Thread(ticket, "小明").start();
new Thread(ticket, "小红").start();
new Thread(ticket, "隔壁老王").start();
}
}
案例:龟兔赛跑-Race
- 首先来个赛道距离,然后要离终点越来越近
- 判断比赛是否结束
- 打印出胜利者
- 龟兔开始赛跑
- 故事中是乌龟赢的,兔子需要睡觉,所以需要模拟兔子睡觉
- 终于,乌龟赢得比赛
package com.moon.demo05;
// 模式龟兔赛跑
public class Race implements Runnable {
private static String winner; // 胜利者
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
String threadName = Thread.currentThread().getName();
// 模拟兔子休息
if (threadName.equals("兔子") && i % 10 == 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 判断比赛是否结束
boolean flag = this.gameOver(i);
if (flag) {
break;
}
System.out.println(threadName + " --> 跑了" + i + "米");
}
}
// 判断是否完成比赛
private boolean gameOver(int meter) {
// 已近存在胜利者
if (winner != null) {
return true;
}
if (meter >= 100) {
Race.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();
}
}
Callable
- 实现 Callable 接口,需要返回值类型
- 重写 call 方法,需要抛出异常
- 创建目标对象
- 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行:Future result1 = ser.submit(t1);
- 获取结果:boolean r1 = result1.get();
- 关闭服务:ser.shutdownNow();
利用 callable 改造下载图片案例
package com.moon.demo06;
import java.util.concurrent.*;
// 练习 Thread,实现多线程同步下载图片
// 实现 callable 接口
/*
callable 优势
1. 可以定义返回值
2. 可以抛出异常
*/
public class TestCallable implements Callable<Boolean> {
private String url; // 网络图片地址
private String name; // 保存的文件名
public TestCallable(String url, String name) {
this.url = url;
this.name = name;
}
// 下载图片线程的执行体
@Override
public Boolean call() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, name);
System.out.println("下载文件" + name + "成功");
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("http://img.netbian.com/file/2021/0310/smallf677a15bc950aef69cec1704ea509cb61615386187.jpg", "01.jpg");
TestCallable t2 = new TestCallable("http://img.netbian.com/file/2021/0326/smallae961e173d8c70abb0107fcc0d5b195b1616774139.jpg", "02.jpg");
TestCallable t3 = new TestCallable("http://img.netbian.com/file/2021/0328/smalle261b1a44ca68a9e5be68aeafe7fe83e1616932954.jpg", "03.jpg");
// 创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
// 提交执行
Future<Boolean> result1 = ser.submit(t1);
Future<Boolean> result2 = ser.submit(t2);
Future<Boolean> result3 = ser.submit(t3);
// 获取结果
boolean r1 = result1.get();
boolean r2 = result2.get();
boolean r3 = result3.get();
System.out.println(r1);
System.out.println(r2);
System.out.println(r3);
// 关闭服务
ser.shutdownNow();
}
}
静态代理
演示:实现静态代理对比 Thread
- 你:真实角色
- 婚庆公司:代理你,帮你处理结婚的事
- 结婚:你和婚庆公司都实现结婚接口即可
package com.moon.demo07;
// 结婚接口
public interface Marry {
void happyMarry();
}
package com.moon.demo07;
// 真实角色(你):你去结婚
public class You implements Marry{
@Override
public void happyMarry() {
System.out.println("我们结婚啦~~");
}
}
package com.moon.demo07;
// 代理角色(婚庆公司):帮助你结婚
public class WeddingCompany implements Marry{
// 代理谁 --> 真实目标角色
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void happyMarry() {
before();
this.target.happyMarry(); // 真实对象
after();
}
private void before() {
System.out.println("结婚之前,布置现场中...");
}
private void after() {
System.out.println("结婚之后,收尾款!!!");
}
}
package com.moon.demo07;
/*
静态代理模式:
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
好处:
代理对象可以做很多真实对象做不了的事情
真实对象可以专注做自己的事情
*/
public class StaticProxy {
public static void main(String[] args) {
//new Thread(new ThreadClassName()).start(); 与 Runnable 开启多线程一致
new WeddingCompany(new You()).happyMarry();
}
}