目录
- 线程简介
- 线程实现(重点)
- 线程状态
- 线程同步(重点)
- 线程通信
- 高级主题
一、线程简介
1. 多线程
- 普通线程和多线程的执行对比
2. 程序、进程和线程
- Process:进程是执行程序的一次执行过程,是一个动态概念,是系统资源分配的单位
- Thread:一个进程中包含多个线程(主线程、GC线程),一个进程至少有一个线程,线程是CPU调度和执行的单位
二、线程创建
1. 线程创建的三种方式
2. 继承Thread类
2.1 多线程基本使用
- 自定义线程类继承Thread类
- 重写
run()
方法,编写执行体 - 创建线程对象,调用
start()
方法启动线程
public class MyThread extends Thread {
public static final int THREAD_LOOP = 20;
/**
* 实现多线程的第一种方式,继承Thread类,重写run方法
*/
@Override
public void run() {
for (int i = 0; i < THREAD_LOOP; i++) {
System.out.println("我在看代码--Thread: " + i);
}
}
/**
* 主方法,创建线程类对象,并调用start方法开始执行
* @param args
*/
public static void main(String[] args) {
MyThread myThread = new MyThread();
// 注意这里是调用start方法,如果调用run则是执行完线程才会执行Main
myThread.start();
for (int i = 0; i < THREAD_LOOP; i++) {
System.out.println("我在写代码--Main: " + i);
}
}
}
我在写代码--Main: 0
我在看代码--Thread: 0
我在看代码--Thread: 1
我在看代码--Thread: 2
我在看代码--Thread: 3
我在看代码--Thread: 4
我在看代码--Thread: 5
我在看代码--Thread: 6
我在看代码--Thread: 7
我在看代码--Thread: 8
我在看代码--Thread: 9
我在写代码--Main: 1
我在写代码--Main: 2
我在看代码--Thread: 10
我在写代码--Main: 3
我在写代码--Main: 4
我在写代码--Main: 5
我在写代码--Main: 6
我在写代码--Main: 7
我在写代码--Main: 8
我在写代码--Main: 9
我在看代码--Thread: 11
我在写代码--Main: 10
我在看代码--Thread: 12
我在写代码--Main: 11
我在看代码--Thread: 13
我在看代码--Thread: 14
我在写代码--Main: 12
我在看代码--Thread: 15
我在写代码--Main: 13
我在看代码--Thread: 16
我在写代码--Main: 14
我在写代码--Main: 15
我在写代码--Main: 16
我在写代码--Main: 17
我在写代码--Main: 18
我在看代码--Thread: 17
我在写代码--Main: 19
我在看代码--Thread: 18
我在看代码--Thread: 19
Process finished with exit code 0
2.2 多线程示例--下载图片
需要引入
commons-io
包:
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
源码:
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* @ClassName DownloadPictureDemo
* @Description 使用多线程下载图片
* @Author wangwk-a
* @Date 2022/1/1 18:28
* @Version 1.0
*/
public class DownloadPictureDemo extends Thread {
public static final String IAMGE_URL_1 = "http://5b0988e595225.cdn.sohucs.com/images/20180621/4b8604d3a7394b33a7462aae6790ff55.jpeg";
public static final String IAMGE_URL_2 = "http://5b0988e595225.cdn.sohucs.com/images/20180621/51b5e16007cb4739a4980ec6e0a50611.jpeg";
public static final String IAMGE_URL_3 = "http://5b0988e595225.cdn.sohucs.com/images/20180621/aeae68a9856744d9a6cddb180be41bcb.jpeg";
/**
* 网络图片地址
*/
private String url;
/**
* 保存的文件名
*/
private String fileName;
public DownloadPictureDemo(String url, String fileName) {
this.url = url;
this.fileName = fileName;
}
/**
* 下载图片的执行体
*/
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, fileName);
System.out.println("下载了文件名为:" + fileName);
}
public static void main(String[] args) {
DownloadPictureDemo th1 = new DownloadPictureDemo(IAMGE_URL_1, "1.jpg");
DownloadPictureDemo th2 = new DownloadPictureDemo(IAMGE_URL_2, "2.jpg");
DownloadPictureDemo th3 = new DownloadPictureDemo(IAMGE_URL_3, "3.jpg");
// 启动线程
th1.start();
th2.start();
th3.start();
}
}
class WebDownloader {
/**
* 下载方法
*/
public void downloader(String url, String fileName) {
try {
FileUtils.copyURLToFile(new URL(url), new File(fileName));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}
下载了文件名为:3.jpg
下载了文件名为:2.jpg
下载了文件名为:1.jpg
Process finished with exit code 0
3. 实现Runnable接口
- 定义线程类实现
Runnable
接口 - 实现
run()
方法,编写线程执行体 - 创建线程对象,调用
start()
方法启动线程(静态代理)
优点:可以避免单继承的局限性,灵活方便,方便同一个对象被多个线程使用
3.1 基本使用
/**
* @ClassName MyThreadImpl
* @Description 通过实现Runnable接口来创建线程
* @Author wangwk-a
* @Date 2022/1/1 18:48
* @Version 1.0
*/
public class MyThreadImpl implements Runnable{
public static final int THREAD_LOOP = 20;
@Override
public void run() {
for (int i = 0; i < THREAD_LOOP; i++) {
System.out.println("我在看代码--Thread: " + i);
}
}
public static void main(String[] args) {
// 执行线程,需要丢入runnable接口实现类,调用start
MyThreadImpl myThread = new MyThreadImpl();
// new Thread是代理,具体实现是MyThreadImpl的run
new Thread(myThread).start();
for (int i = 0; i < THREAD_LOOP; i++) {
System.out.println("我在写代码--Main: " + i);
}
}
}
我在写代码--Main: 0
我在写代码--Main: 1
我在写代码--Main: 2
我在看代码--Thread: 0
我在看代码--Thread: 1
我在写代码--Main: 3
我在写代码--Main: 4
我在看代码--Thread: 2
我在写代码--Main: 5
我在看代码--Thread: 3
我在写代码--Main: 6
我在写代码--Main: 7
我在写代码--Main: 8
我在看代码--Thread: 4
我在写代码--Main: 9
我在看代码--Thread: 5
我在写代码--Main: 10
我在看代码--Thread: 6
我在看代码--Thread: 7
我在写代码--Main: 11
我在写代码--Main: 12
我在写代码--Main: 13
我在写代码--Main: 14
我在写代码--Main: 15
我在写代码--Main: 16
我在写代码--Main: 17
我在写代码--Main: 18
我在写代码--Main: 19
我在看代码--Thread: 8
我在看代码--Thread: 9
我在看代码--Thread: 10
我在看代码--Thread: 11
我在看代码--Thread: 12
我在看代码--Thread: 13
我在看代码--Thread: 14
我在看代码--Thread: 15
我在看代码--Thread: 16
我在看代码--Thread: 17
我在看代码--Thread: 18
我在看代码--Thread: 19
Process finished with exit code 0
3.2 一个对象被多个线程调用示例
/**
* @ClassName ThreadMultiCall
* @Description 测试多个线程操作同一个对象
* @Author wangwk-a
* @Date 2022/1/1 18:54
* @Version 1.0
*/
public class ThreadMultiCall implements Runnable{
/**
* 火车票数
*/
private int ticketNums = 10;
@Override
public void run() {
while (true) {
if (ticketNums <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNums-- + "张票");
}
}
public static void main(String[] args) {
ThreadMultiCall th = new ThreadMultiCall();
// 多个线程操作一个对象
new Thread(th, "线程1").start();
new Thread(th, "线程2").start();
new Thread(th, "线程3").start();
}
}
线程2拿到了第10张票
线程2拿到了第7张票
线程2拿到了第6张票
线程2拿到了第5张票
线程2拿到了第4张票
线程2拿到了第3张票
线程2拿到了第2张票
线程1拿到了第9张票
线程3拿到了第8张票
线程2拿到了第1张票
多个线程操作同一个资源的时候,是线程不安全的,可能会出现多个线程获取相同的资源情况,这个在后续的线程并发中解决
4. 实现Callable接口(扩充)
- 实现
Callable
接口,需要返回值的类型 - 重写
call
f方法,需要抛出异常 - 创建目标对象
- 创建执行服务
ExecutorServive
(线程池) - 提交执行
submit
- 获取结果
- 关闭服务
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
/**
* @ClassName ThreadImplCallableMethod
* @Description TODO
* @Author wangwk-a
* @Date 2022/1/1 19:04
* @Version 1.0
*/
public class ThreadImplCallableMethod implements Callable {
public static final String IAMGE_URL_1 = "http://5b0988e595225.cdn.sohucs.com/images/20180621/4b8604d3a7394b33a7462aae6790ff55.jpeg";
public static final String IAMGE_URL_2 = "http://5b0988e595225.cdn.sohucs.com/images/20180621/51b5e16007cb4739a4980ec6e0a50611.jpeg";
public static final String IAMGE_URL_3 = "http://5b0988e595225.cdn.sohucs.com/images/20180621/aeae68a9856744d9a6cddb180be41bcb.jpeg";
/**
* 网络图片地址
*/
private String url;
/**
* 保存的文件名
*/
private String fileName;
public ThreadImplCallableMethod(String url, String fileName) {
this.url = url;
this.fileName = fileName;
}
@Override
public Boolean call() throws Exception {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url, fileName);
System.out.println("下载了文件名为:" + fileName);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadImplCallableMethod th1 = new ThreadImplCallableMethod(IAMGE_URL_1, "1.jpg");
ThreadImplCallableMethod th2 = new ThreadImplCallableMethod(IAMGE_URL_2, "2.jpg");
ThreadImplCallableMethod th3 = new ThreadImplCallableMethod(IAMGE_URL_3, "3.jpg");
/* TODO: 1.创建执行服务 */
ExecutorService service = Executors.newFixedThreadPool(3);
/* TODO: 2.提交执行 */
Future<Boolean> submit1 = service.submit(th1);
Future<Boolean> submit2 = service.submit(th2);
Future<Boolean> submit3 = service.submit(th3);
/* TODO: 3.获取结果 */
Boolean r1 = submit1.get();
Boolean r2 = submit1.get();
Boolean r3 = submit1.get();
/* TODO: 4.关闭服务 */
service.shutdown();
}
}
class WebDownloader {
/**
* 下载方法
*/
public void downloader(String url, String fileName) {
try {
FileUtils.copyURLToFile(new URL(url), new File(fileName));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}
}
}
下载了文件名为:2.jpg
下载了文件名为:3.jpg
下载了文件名为:1.jpg
Process finished with exit code 0