线程
初识线程
程序.进程.线程
- 程序 是 指令和数据的有序集合 ,是一个静态的概念。
- 进程 是 执行程序的依次执行过程 ,是一个动态的概念。是系统资源分配的单位。
- 一个进程中可以包含若干个 线程 ,一个进程中至少有一个线程。线程是CPU调度和执行的单位。
线程创建
创建线程四种方法
- 继承Thread类,重写run()方法
- 实现Runnable()接口,重写run()方法
- 实现Callabl()接口,重写call()方法
- 线程池
1. 继承Thread类
不推荐使用,避免OOP单继承的局限性
自定义线程类继承 ‘Thread’ 类
重写 ‘run()’ 方法,编写线程执行体
创建线程对象,调用 ‘start()’ 方法启动线程
线程不一定立即执行,CPU安排调度
/**
* 线程实现方式一:
* 1. 继承Thread类
* 2. 重写run方法
* 3. 调用start开启线程
*
* 不推荐使用,避免OOP单继承的局限性
*/
public class ThreadExtends extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我喝了" + (i + 1) + "口水!");
}
}
public static void main(String[] args) {
ThreadExtends threadExtends = new ThreadExtends();
// 只有 主线程main 一条执行路径
// run = 插队
// threadExtends.run();
// 多条执行路径
// start = 开辟新的执行路径
// 主线程main和子线程run交替执行
threadExtends.start();
for (int i = 0; i < 100; i++) {
System.out.println("第吃了" + (i + 1) + "口饭!");
}
}
}
/**
* 继承Thread类实现多线程下载图片
*/
public class ThreadExtendsDemo extends Thread {
public String url;
public String name;
public ThreadExtendsDemo(String url, String name) {
this.url = url;
this.name = name;
}
/**
* 下载图片线程的执行体
*/
@Override
public void run() {
// 重写run方法
WebDownloader webDownloader = new WebDownloader();
webDownloader.downLoad(url, name);
System.out.println("下载了文件名为:" + name);
}
public static void main(String[] args) {
ThreadExtendsDemo t1 = new ThreadExtendsDemo("http://cms-bucket.ws.126.net/2019/12/19/cf143f1465dc403c86cb7dc5e86a2c90.png", "WY_logo.jpg");
ThreadExtendsDemo t2 = new ThreadExtendsDemo("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "baidu_log.jpg");
ThreadExtendsDemo t3 = new ThreadExtendsDemo("https://docs.alibabagroup.com/assets2/images/cn/global/logo_header.png", "alibaba-log.jpg");
/**
* 线程不一定立即执行,CPU安排调度
*/
t1.start();
t2.start();
t3.start();
}
}
// 下载器
class WebDownloader {
// 下载方法
public void downLoad(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoad方法下载失败!");
}
}
}
2. 实现Runnable()接口
推荐使用Runnable对象,因为Java单继承的局限性
自定义线程类实现 Runnable 接口
实现 run() 方法,编写线程执行体
创建线程对象
丢入 Runnable 接口实现类,调用 start 开启线程(静态代理)
/**
* 线程实现方式二:
* 1. 实现Runnable接口
* 2. 重写run方法
* 3. 执行线程需要丢入Runnable接口实现类,调用start开启线程
*
* 推荐使用,避免了单继承的局限性,方便了同一个对象被多个线程使用
*/
public class ThreadRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我喝了" + (i + 1) + "口水!");
}
}
public static void main(String[] args) {
// 创建Runnable接口的实现类对象
ThreadRunnable threadRunnable = new ThreadRunnable();
// 创建线程对象,通过线程对象开启我们的线程。
// 静态代理
new Thread(threadRunnable).start();
for (int i = 0; i < 100; i++) {
System.out.println("第吃了" + (i + 1) + "口饭!");
}
}
}
/**
* 多个线程操作同一个对象
* 买火车票
*/
public class ThreadRunnableTicketDemo implements Runnable {
// 票数
private int ticketNum = 10;
@Override
public void run() {
while (true) {
if (ticketNum <= 0) {
break;
}
// 模拟延时:提高问题的发生性
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " --> 拿到了第" + ticketNum-- + "张票");
}
}
public static void main(String[] args) {
ThreadRunnableTicketDemo ticketThread = new ThreadRunnableTicketDemo();
new Thread(ticketThread, "张三").start();
new Thread(ticketThread, "李四").start();
new Thread(ticketThread, "黄牛党").start();
// 发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱。
}
}
3. 实现Callable()接口
- 实现 Callable 接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建线程目标对象
- 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(3);
- 提交执行:Future result1 = ser.submit(线程目标对象);
- 获取结果:boolean r1 = result1.get()
- 关闭服务:ser.shutdownNow();
好处:
可以定义返回值
可以抛出异常
/**
* 线程实现方式三:
* 1. 实现Callable接口
* 2. 重写call方法
* 3. 创建目标对象
* 4. 创建执行服务 ExecutorService service = Executors.newFixedThreadPool(3);
* 5. 提交执行 Future<Boolean> submit1 = service.submit(t1);
* 6. 获取执行结果 Boolean aBoolean1 = submit1.get();
* 7. 关闭服务 service.shutdownNow();
* <p>
* 好处:
* 1. 可以定义返回值
* 2. 可以抛出异常
*/
public class ThreadCallable implements Callable<Boolean> {
public String url;
public String name;
public ThreadCallable(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() throws Exception {
// 重写run方法
WebDownloader webDownloader = new WebDownloader();
webDownloader.downLoad(url, name);
System.out.println("下载了文件名为:" + name);
return true;
}
public static void main(String[] args) {
ThreadCallable t1 = new ThreadCallable("http://cms-bucket.ws.126.net/2019/12/19/cf143f1465dc403c86cb7dc5e86a2c90.png", "WY_logo.jpg");
ThreadCallable t2 = new ThreadCallable("https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png", "baidu_log.jpg");
ThreadCallable t3 = new ThreadCallable("https://docs.alibabagroup.com/assets2/images/cn/global/logo_header.png", "alibaba-log.jpg");
// 创建执行服务
ExecutorService service = Executors.newFixedThreadPool(3);
// 提交执行
Future<Boolean> submit1 = service.submit(t1);
Future<Boolean> submit2 = service.submit(t2);
Future<Boolean> submit3 = service.submit(t3);
try {
// 获取执行结果
Boolean aBoolean1 = submit1.get();
Boolean aBoolean2 = submit2.get();
Boolean aBoolean3 = submit3.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
// 关闭服务
service.shutdownNow();
}
}
}
// 下载器
class WebDownloader {
// 下载方法
public void downLoad(String url, String name) {
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoad方法下载失败!");
}
}
}
4. 线程池(JUC)
线程池提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提交了响应速度。
- 背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
- 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。
可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。- 好处:
* 提高响应速度(减少了创建新线程的时间)
* 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
* 便于线程管理…
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime::线程没有任务时最多保持多长时间后会终止
- JDK 5.0起提供了线程池相关API: ExecutorService 和Executors
- ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
- void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执Runnable
- Future submit(Callable task):执行任务,有返回值,一般用来执行Callable
- void shutdown() :关闭连接池
- Executors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池
/**
* 测试线程池
* service.execute(threadPool);
* 执行任务/命令,没有返回值,一般用来执行Runnable
*/
public class ThreadPool {
public static void main(String[] args) {
// 1. 创建线程池服务
// newFixedThreadPool(线程池大小)
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadRunnable threadRunnable = new ThreadRunnable();
// 2. 执行
service.execute(threadRunnable);
service.execute(threadRunnable);
service.execute(threadRunnable);
service.execute(threadRunnable);
service.execute(threadRunnable);
//3. 关闭服务
service.shutdown();
}
}
class ThreadRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
/**
* 测试线程池
* service.submit(threadCallable);
* 执行任务,有返回值,一般用来执行Callable
*/
public class ThreadPool2 {
public static void main(String[] args) {
// 1. 创建线程池服务
// newFixedThreadPool(线程池大小)
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadCallable threadCallable = new ThreadCallable();
// 2. 提交执行
Future<Boolean> submit1 = service.submit(threadCallable);
Future<Boolean> submit2 = service.submit(threadCallable);
Future<Boolean> submit3 = service.submit(threadCallable);
Future<Boolean> submit4 = service.submit(threadCallable);
Future<Boolean> submit5 = service.submit(threadCallable);
//3. 关闭服务
service.shutdown();
}
}
class ThreadCallable implements Callable<Boolean> {
@Override
public Boolean call() {
System.out.println(Thread.currentThread().getName());
return true;
}
}
静态代理
举例:婚庆公司代理结婚对象,处理结婚事宜。
/**
* 静态代理
*
* 真实对象和代理对象都要实现同一个接口
* 代理对象必须要代理真实对象
*
* 好处:
* 代理对象可以做很多真实对象做不了的事情
* 真实对象专注做自己的事情
*/
public class StaticProxy {
public static void main(String[] args) {
// 你要结婚
You you = new You();
// 婚庆公司帮助你结婚
/*WeddingCompany weddingCompany = new WeddingCompany(you);
weddingCompany.happyMarry();*/
// 对比: Thread就是个代理对象
new Thread(() -> System.out.println("我爱你!")).start();
new WeddingCompany(you).happyMarry();
}
}
/**
* 结婚接口
*/
interface Marry {
void happyMarry();
}
/**
* 真实结婚对象,你去结婚
*/
class You implements Marry {
@Override
public void happyMarry() {
System.out.println("张三结婚了,很开心!");
}
}
/**
* 代理结婚对象,帮助你结婚
*/
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 after() {
System.out.println("结婚之后,收尾款!");
}
private void before() {
System.out.println("结婚之前,布置现场!");
}
}
总结
真实对象和代理对象都要实现一个接口
代理对象要代理真实角色
好处
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事
汇总:线程结合lamda使用其实也是静态代理
new Thread(() -> System.out.println("我爱你!")).start();
Lamda
Lamda来由
先看Lamda解决了什么问题:
- 避免匿名内部类定义过多
- 其实质属于函数式编程的概念
- 去掉了一堆没有意义的代码,只留下核心逻辑
学习Lamde首先要提到 函数式接口(Functional Interface) ,这是学习Java8 lamda表达式的关键
函数式接口
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
# 例如Runnable接口,就是一个函数式接口
public interface Runnable{
public abstract void run();
}
Lamda表达式推导
public class LambdaWithNotParameter {
// 3. 静态内部类
static class StaticInnerClasses implements ILike {
@Override
public void lambda() {
System.out.println("我是静态内部类");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
like = new StaticInnerClasses();
like.lambda();
// 4. 局部内部类
class LocalInnerClasses implements ILike {
@Override
public void lambda() {
System.out.println("我是局部内部类");
}
}
like = new LocalInnerClasses();
like.lambda();
// 5. 匿名内部类,没有类的名称,必须借助接口或者父类
like = new ILike() {
@Override
public void lambda() {
System.out.println("我是匿名内部类");
}
};
like.lambda();
// 6. Lambda简化
like = () -> {
System.out.println("我是Lambda");
};
like.lambda();
}
}
/**
* 1. 定义函数式接口
* 接口中只有一个方法的接口
*/
interface ILike {
void lambda();
}
/**
* 2. 实现类
*/
class Like implements ILike {
@Override
public void lambda() {
System.out.println("我是内部类");
}
}
总结
学习完以上内容,我学会了:
- 创建线程的四种方式,分别是继承Thread类,实现Runnable接口,实现Callable,线程池创建。
- 体会到线程的静态代理模式。
- 懂了Lambda表达式。