一.java多线程基本概念
调用run():在主线程调用子线程的run()方法会中断主线程等到子线程执行完毕之后再执行主线程。
调用start():在主线程中执行子线程的start()后会与主线程同步执行。
二.线程创建的三种方法
Thread、Runnable、Callable
1.Thread入门
步骤:
- 继承Thread类
- 重写run方法
- 调用start开启线程
1 //创建线程方式一步骤:继承Thread类,重写run方法,调用start开启线程 2 public class test01 extends Thread{ 3 4 5 @Override 6 public void run() { 7 for (int i = 0; i < 10;i++){ 8 System.out.println("run方法执行..." + i); 9 } 10 } 11 12 public static void main(String[] args) { 13 14 //创建线程对象 15 test01 t1 = new test01(); 16 17 //开启线程 18 t1.start(); 19 20 for (int i = 0; i < 1000; i++){ 21 System.out.println("main方法执行..." + i); 22 } 23 } 24 }
执行结果:交替执行
练习:使用Thread实现多线程同步下载图片
(1).导入依赖的jar包:
1 <dependency> 2 <groupId>commons-io</groupId> 3 <artifactId>commons-io</artifactId> 4 <version>2.6</version> 5 </dependency>
(2).编写test02.java:
1 /** 2 * 使用多线程下载图片 3 * @author USTC_WZH 4 * @create 2019-12-05 11:26 5 */ 6 public class test02 extends Thread { 7 8 9 //图片url地址 10 private String url; 11 //保存的文件名 12 private String name; 13 14 public test02(String url, String name) { 15 this.url = url; 16 this.name = name; 17 } 18 19 @Override 20 public void run() { 21 WebDownloader webDownloader = new WebDownloader(); 22 webDownloader.downloader(url, name); 23 System.out.println("下载文件名:" + name); 24 } 25 26 public static void main(String[] args) { 27 test02 t1 = new test02("http://lswhw.ustc.edu.cn/upload/20150427/20150427102635.jpg", "images/图片1.jpg"); 28 test02 t2 = new test02("http://lswhw.ustc.edu.cn/upload/20150427/20150427103657.jpg", "images/图片2.jpg"); 29 test02 t3 = new test02("http://lswhw.ustc.edu.cn/upload/20150427/20150427103010.jpg", "images/图片3.jpg"); 30 31 t1.start(); 32 t2.start(); 33 t3.start(); 34 } 35 } 36 37 38 class WebDownloader { 39 40 public void downloader(String url, String name) { 41 try { 42 FileUtils.copyURLToFile(new URL(url), new File(name)); 43 } catch (IOException e) { 44 e.printStackTrace(); 45 System.out.println("downloader方法出现异常"); 46 } 47 } 48 }
2.实现Runable接口(重点使用)
三个步骤:
- 实现Runnable接口,重写run方法
- 执行线程需要丢入Runnable接口的实现类
- 调用start方法
1 /** 2 * 创建线程方法二步骤: 3 * 1.实现Runnable接口,重写run方法。 4 * 2.执行线程需要丢入runnable接口的实现类。 5 * 3.调用start方法。 6 * @author USTC_WZH 7 * @create 2019-12-05 14:14 8 */ 9 public class test03 implements Runnable{ 10 11 @Override 12 public void run() { 13 for (int i = 0; i < 10;i++){ 14 System.out.println("run方法执行..." + i); 15 } 16 } 17 18 public static void main(String[] args) { 19 20 //创建runnable接口的实现类对象 21 test01 t1 = new test01(); 22 23 //创建线程对象,通过线程对象来开启我们的线程,代理模式 24 new Thread(t1).start(); 25 26 for (int i = 0; i < 1000; i++){ 27 System.out.println("main方法执行..." + i); 28 } 29 } 30 }
补充:模拟并发问题
1 /** 2 * 模拟买火车票 3 * 4 * @author USTC_WZH 5 * @create 2019-12-05 14:32 6 */ 7 public class testerror implements Runnable { 8 9 //火车票个数 10 private int ticketNums = 10; 11 12 @Override 13 public void run() { 14 15 while (true) { 16 17 if (ticketNums <= 0) { 18 break; 19 } 20 21 //模拟延时 22 try { 23 Thread.sleep(100); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 28 System.out.println(Thread.currentThread().getName() + "-->拿了第" + ticketNums-- + "票"); 29 } 30 } 31 32 public static void main(String[] args) { 33 34 testerror t = new testerror(); 35 36 new Thread(t, "小明").start(); 37 new Thread(t, "小红").start(); 38 new Thread(t, "花花").start(); 39 } 40 }
当同一个对象被多个线程访问时出现的并发错误的问题!
3.实现Callable接口(了解)
步骤:7步
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务
- 提交执行
- 获取结果
- 关闭服务
1 package ustc.wzh.callable; 2 3 import org.apache.commons.io.FileUtils; 4 5 import java.io.File; 6 import java.io.IOException; 7 import java.net.URL; 8 import java.util.concurrent.*; 9 10 /** 11 * @author USTC_WZH 12 * @create 2019-12-05 14:44 13 * <p> 14 * 创建线程方式三步骤: 15 * 1.实现Callable接口,需要返回值类型 16 * 2.重写call方法,需要抛出异常 17 * 3.创建目标对象 18 * 4.创建执行服务 19 * 5.提交执行 20 * 6.获取结果 21 * 7.关闭服务 22 */ 23 //1.实现Callable接口 24 public class testCallable implements Callable<Boolean> { 25 26 //图片url地址 27 private String url; 28 //保存的文件名 29 private String name; 30 31 public testCallable(String url, String name) { 32 this.url = url; 33 this.name = name; 34 } 35 36 37 //2.重写call方法 38 @Override 39 public Boolean call() throws Exception { 40 WebDownloader webDownloader = new WebDownloader(); 41 webDownloader.downloader(url, name); 42 System.out.println("下载文件名:" + name); 43 return true; 44 } 45 46 public static void main(String[] args) throws ExecutionException, InterruptedException { 47 48 //创建目标对象 49 testCallable t1 = new testCallable("http://lswhw.ustc.edu.cn/upload/20150427/20150427102635.jpg", "images/图片1.jpg"); 50 testCallable t2 = new testCallable("http://lswhw.ustc.edu.cn/upload/20150427/20150427103657.jpg", "images/图片2.jpg"); 51 testCallable t3 = new testCallable("http://lswhw.ustc.edu.cn/upload/20150427/20150427103010.jpg", "images/图片3.jpg"); 52 53 //4.创建执行服务 54 ExecutorService ser = Executors.newFixedThreadPool(3); 55 56 //5.提交执行 57 Future<Boolean> r1 = ser.submit(t1); 58 Future<Boolean> r2 = ser.submit(t2); 59 Future<Boolean> r3 = ser.submit(t3); 60 61 //6.获取结果 62 boolean rs1 = r1.get(); 63 boolean rs2 = r2.get(); 64 boolean rs3 = r3.get(); 65 66 System.out.println("rs1=" + rs1 + ",rs2=" + rs2 + ",rs3=" + rs3); 67 68 //7.关闭服务 69 ser.shutdownNow(); 70 } 71 72 73 } 74 75 class WebDownloader { 76 77 public void downloader(String url, String name) { 78 try { 79 FileUtils.copyURLToFile(new URL(url), new File(name)); 80 } catch (IOException e) { 81 e.printStackTrace(); 82 System.out.println("downloader方法出现异常"); 83 } 84 } 85 }