一.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 }