线程的创建的三种方式
1.继承Thread类(重点)
实现方法:
- 自定义线程类继承Thread类
- 重写**run()**方法,编写线程执行体
- 创建线程对象,调用**start()**方法启动线程
//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
public class TestThread1 extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码---" + i);
}
}
public static void main(String[] args) {
//创建一个线程对象
TestThread1 testThread1 = new TestThread1();
//调用start方法开启线程
testThread1.start();
for (int i = 0; i < 200; i++) {
System.out.println("我在学习多线程---" + i);
}
}
}
总结:注意,线程开启不一定立即执行,由cpu调度执行
不建议使用:避免OOP单继承的局限性
多线程同步下载图片
需求:练习Thread,实现多线程同步下载图片
- 将commons.io包下载并导入lib目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzSJQpc5-1628670582068)(C:\Users\zhou\AppData\Roaming\Typora\typora-user-images\image-20210810161719664.png)]
-
编写下载器
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方法出现问题"); } } }
-
编写下载图片线程run()方法的执行体
private String url; private String name; public TestThread2(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); }
调用start()启动线程
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://img2.baidu.com/it/u=2379421903,2766120725&fm=26&fmt=auto&gp=0.jpg", "1.jpg");
TestThread2 t2 = new TestThread2("https://img0.baidu.com/it/u=2580887492,2953392680&fm=26&fmt=auto&gp=0.jpg", "2.jpg");
TestThread2 t3 = new TestThread2("https://img2.baidu.com/it/u=3562631285,4033075858&fm=26&fmt=auto&gp=0.jpg", "3.jpg");
//先下载t1
t1.start();
//然后是t2
t2.start();
//最后是t3
t3.start();
}
执行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B5EY6nuD-1628670582069)(C:\Users\zhou\AppData\Roaming\Typora\typora-user-images\image-20210810163317143.png)]
查看文件,已经存在1.jpg, 2.jpg, 3.jpg
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7R3SyQiv-1628670582070)(C:\Users\zhou\AppData\Roaming\Typora\typora-user-images\image-20210810163349613.png)]
2.实现Runnable接口(重点)
实现方法:
- 定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
//创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢人runnable接口实现类,调用start方法
public class TestThread3 implements Runnable{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码---" + i);
}
}
public static void main(String[] args) {
//创建runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();
//创建线程对象,通过线程对象来开启线程
//Thread thread = new Thread(testThread3);
//thread.start();
new Thread(testThread3).start();
for (int i = 0; i < 200; i++) {
System.out.println("我在学习多线程---" + i);
}
}
}
总结:推荐使用,避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
并发问题初识—火车抢票案例
火车票抢票问题:
public class TestThread4 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();
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
}
}
public static void main(String[] args) {
TestThread4 ticket = new TestThread4();
new Thread(ticket, "小明").start();
new Thread(ticket, "老师").start();
new Thread(ticket, "黄牛党").start();
}
}
发现问题:当多个资源操作同一个资源的情况下,线程不安全,数据紊乱
龟兔赛跑案例-Race
//模拟龟兔赛跑
public class Race implements Runnable{
//胜利者
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子休息
if (Thread.currentThread().getName().equals("兔子")&& i % 10 == 0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
//如果比赛结束了,就停止程序
if (flag) {
break;
}
System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
}
}
//判断是否完成比赛
private boolean gameOver(int steps) {
//判断是否有胜利者
if (winner != null) { //已经存在胜利者
return true;
}
if (steps >= 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();
}
}
输出结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qExHuyFs-1628670582071)(C:\Users\zhou\AppData\Roaming\Typora\typora-user-images\image-20210810171301805.png)]
3.实现Callable接口(了解)
-
实现Callable接口,需要返回值类型
-
重写call方法,需要抛出异常
-
创建目标对象
-
创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(3);
-
提交执行:Future r1 = ser.submit(t1);
-
获取结果:boolean rs1 = r1.get();
-
关闭服务:ser.shutdownNow();
演示:用Callable改造下载图片案例
import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.concurrent.*; //线程创建方式三:实现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("https://img2.baidu.com/it/u=2379421903,2766120725&fm=26&fmt=auto&gp=0.jpg", "1.jpg"); TestCallable t2 = new TestCallable("https://img0.baidu.com/it/u=2580887492,2953392680&fm=26&fmt=auto&gp=0.jpg", "2.jpg"); TestCallable t3 = new TestCallable("https://img2.baidu.com/it/u=3562631285,4033075858&fm=26&fmt=auto&gp=0.jpg", "3.jpg"); //创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(3); //提交执行 Future<Boolean> r1 = ser.submit(t1); Future<Boolean> r2 = ser.submit(t2); Future<Boolean> r3 = ser.submit(t3); //获取结果 boolean rs1 = r1.get(); boolean rs2 = r2.get(); boolean rs3 = r3.get(); System.out.println(rs1); System.out.println(rs2); System.out.println(rs3); //关闭服务 ser.shutdownNow(); } } //下载器 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方法出现问题"); } } }