【day18】多线程高级应用
【day18】多线程高级应用
day17回顾
在深入探讨模块18之前,让我们回顾一下【day17】中的关键内容:
- 创建多线程:
- 继承
Thread
类:- 定义一个类,继承
Thread
。 - 重写
run
方法,设置线程任务。 - 创建自定义线程对象。
- 调用
start
方法,开启线程,JVM自动执行run
方法。
- 定义一个类,继承
- 实现
Runnable
接口:- 定义一个类,实现
Runnable
。 - 重写
run
方法,设置线程任务。 - 创建自定义线程对象,传递到
Thread
对象中。 - 调用
start
方法,开启线程,JVM自动调用run
方法。
- 定义一个类,实现
- 匿名内部类形式创建:
new Thread(new Runnable(){...}).start();
- 继承
-
Thread
中的方法:start()
:开启线程,JVM自动调用run
方法。getame()
:获取线程名字。setame(String name)
:设置线程名字。currentThread()
:获取当前正在执行的线程对象。sleep(long time)
:线程睡眠。setPriority(int n)
:设置线程优先级。getPriority()
:获取线程优先级。setDaemon(true)
:设置为守护线程。yield()
:礼让线程。join()
:插队线程。
- 线程安全:
- 同步代码块:
synchronized(锁对象){...}
- 同步方法:在定义方法的时候加上
synchronized
关键字。- 非静态:默认锁
this
。 - 静态的:默认锁
class
对象。
- 非静态:默认锁
- 同步代码块:
模块17重点
本模块将深入探讨多线程的高级应用,包括:
- 使用
wait
和notify
两个方法。 - 使用
Lock
锁对象。 - 利用
Callable
接口实现多线程。 - 使用线程池完成多线程。
第一章:等待唤醒机制
1.等待唤醒案例分析(线程之间的通信)
在生产者-消费者模型中,我们需要协调线程之间的通信,确保生产和消费的同步进行。这就需要用到等待唤醒机制。
wait()
:线程等待,等待的过程中线程会释放锁,需要被其他线程调用notify
方法将其唤醒,重新抢锁执行。notify()
:线程唤醒,一次唤醒一个等待线程;如果有多条线程等待,则随机唤醒一条等待线程。notifyAll()
:唤醒所有等待线程。
wait
和notify
方法需要锁对象调用,所以需要用到同步代码块中,而且必须是同一个锁对象。
2.等待唤醒案例实现
以下是生产者-消费者模型的实现代码:
代码语言:javascript代码运行次数:0运行复制/*
count和flag可以定义成包装类
但是要记得给count和flag手动赋值
不然对于本案例来说,容易出现空指针异常
*/
public class BaoZiPu {
//代表包子的count
private int count;
//代表是否有包子的flag
private boolean flag;
public BaoZiPu() {
}
public BaoZiPu(int count, boolean flag) {
= count;
this.flag = flag;
}
/*
getCount 改造成消费包子方法
直接输出count
*/
public void getCount() {
println("消费了..............第"+count+"个包子");
}
/*
setCount 改造成生产包子
count++
*/
public void setCount() {
count++;
println("生产了...第"+count+"个包子");
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
代码语言:javascript代码运行次数:0运行复制public class Product implements Runnable{
private BaoZiPu baoZiPu;
public Product(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (baoZiPu){
//1.判断flag是否为true,如果是true,证明有包子,生产线程等待
if (baoZiPu.isFlag()==true){
try {
baoZiPu.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//2.如果flag为false,证明没有包子,开始生产
baoZiPu.setCount();
//.改变flag状态,为true,证明生产完了,有包子了
baoZiPu.setFlag(true);
//4.唤醒消费线程
();
}
}
}
}
代码语言:javascript代码运行次数:0运行复制public class Cumer implements Runnable{
private BaoZiPu baoZiPu;
public Cumer(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (baoZiPu){
//1.判断flag是否为false,如果是false,证明没有包子,消费线程等待
if (baoZiPu.isFlag()==false){
try {
baoZiPu.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//2.如果flag为true,证明有包子,开始消费
baoZiPu.getCount();
//.改变flag状态,为false,证明消费完了,没 有包子了
baoZiPu.setFlag(false);
//4.唤醒生产线程
();
}
}
}
}
代码语言:javascript代码运行次数:0运行复制public class Test01 {
public static void main(String[] args) {
BaoZiPu baoZiPu = new BaoZiPu();
Product product = new Product(baoZiPu);
Cumer cumer = new Cumer(baoZiPu);
Thread t1 = new Thread(product);
Thread t2 = new Thread(cumer);
t1.start();
t2.start();
}
}
第二章:多等待多唤醒
1.解决多生产多消费问题
在多生产者和多消费者的情况下,我们需要使用while
循环来替代if
判断,并将notify
改为notifyAll
,以确保所有等待的线程都能被唤醒。
public class Test01 {
public static void main(String[] args) {
BaoZiPu baoZiPu = new BaoZiPu();
Product product = new Product(baoZiPu);
Cumer cumer = new Cumer(baoZiPu);
new Thread(product).start();
new Thread(product).start();
new Thread(product).start();
new Thread(cumer).start();
new Thread(cumer).start();
new Thread(cumer).start();
}
}
代码语言:javascript代码运行次数:0运行复制/*
count和flag可以定义成包装类
但是要记得给count和flag手动赋值
不然对于本案例来说,容易出现空指针异常
*/
public class BaoZiPu {
//代表包子的count
private int count;
//代表是否有包子的flag
private boolean flag;
public BaoZiPu() {
}
public BaoZiPu(int count, boolean flag) {
= count;
this.flag = flag;
}
/*
getCount 改造成消费包子方法
直接输出count
*/
public synchronized void getCount() {
//1.判断flag是否为false,如果是false,证明没有包子,消费线程等待
while (this.flag == false) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//2.如果flag为true,证明有包子,开始消费
println("消费了..............第" + count + "个包子");
//.改变flag状态,为false,证明消费完了,没 有包子了
this.flag = false;
//4.唤醒所有等待线程
();
}
/*
setCount 改造成生产包子
count++
*/
public synchronized void setCount() {
//1.判断flag是否为true,如果是true,证明有包子,生产线程等待
while (this.flag == true) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//2.如果flag为false,证明没有包子,开始生产
count++;
println("生产了...第" + count + "个包子");
//.改变flag状态,为true,证明生产完了,有包子了
this.flag = true;
//4.唤醒所有等待线程
();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
代码语言:javascript代码运行次数:0运行复制public class Product implements Runnable{
private BaoZiPu baoZiPu;
public Product(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
baoZiPu.setCount();
}
}
}
代码语言:javascript代码运行次数:0运行复制public class Cumer implements Runnable{
private BaoZiPu baoZiPu;
public Cumer(BaoZiPu baoZiPu) {
this.baoZiPu = baoZiPu;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
baoZiPu.getCount();
}
}
}
第三章:Lock锁
1.Lock对象的介绍和基本使用
Lock
是一个接口,其实现类为ReentrantLock
,提供了比synchronized
更灵活的锁控制。
lock()
:获取锁。unlock()
:释放锁。
public class MyTicket implements Runnable {
//定义100张票
int ticket = 100;
//创建Lock对象
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
Thread.sleep(100L);
//获取锁
lock.lock();
if (ticket > 0) {
println(().getame() + "买了第" + ticket + "张票");
ticket--;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
//释放锁
lock.unlock();
}
}
}
}
代码语言:javascript代码运行次数:0运行复制public class Test01 {
public static void main(String[] args) {
MyTicket myTicket = new MyTicket();
Thread t1 = new Thread(myTicket, "赵四");
Thread t2 = new Thread(myTicket, "刘能");
Thread t = new Thread(myTicket, "广坤");
t1.start();
t2.start();
t.start();
}
}
synchronized
: 不管是同步代码块还是同步方法,都需要在结束一对{}之后,释放锁对象
Lock
: 是通过两个方法控制需要被同步的代码,更灵活
第四章:Callable接口_实现多线程方式三
Callable
接口是实现多线程的另一种方式,它允许任务返回值。
call()
:设置线程任务的方法,类似于run
方法,但可以返回值和抛出异常。
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "涛哥和金莲...的故事";
}
}
代码语言:javascript代码运行次数:0运行复制public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
/*
FutureTask(Callable<V> callable)
*/
FutureTask<String> futureTask = new FutureTask<>(myCallable);
//创建Thread对象-> Thread(Runnable target)
Thread t1 = new Thread(futureTask);
t1.start();
//调用get方法获取call方法返回值
println(futureTask.get());
}
}
第五章:线程池_实现多线程方式四
问题:之前来一个线程任务,就需要创建一个线程对象去执行,用完还要销毁线程对象,如果线程任务多了,就需要频繁创建线程对象和销毁线程对象,这样会耗费内存资源,所以我们就想线程对象能不能循环利用,用的时候直接拿线程对象,用完还回去
线程池是管理线程对象的一种机制,可以循环利用线程对象,减少创建和销毁线程对象的开销。
(int nThreads)
:创建固定大小的线程池。submit(Runnable task)
和submit(Callable<T> task)
:提交任务到线程池执行。Future.get()
:获取任务的返回值。
public class MyRunnable implements Runnable{
@Override
public void run() {
println(().getame()+"...执行了");
}
}
代码语言:javascript代码运行次数:0运行复制public class Test01 {
public static void main(String[] args) {
//创建线程池对象
ExecutorService es = (2);
es.submit(new MyRunnable());
es.submit(new MyRunnable());
es.submit(new MyRunnable());
//es.shutdown();//关闭线程池对象
}
}
代码语言:javascript代码运行次数:0运行复制public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 1;
}
}
代码语言:javascript代码运行次数:0运行复制public class Test02 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService es = (2);
Future<Integer> future = es.submit(new MyCallable());
println(future.get());
}
}
练习
需求:创建两个线程任务,一个线程任务完成1-100的和,一个线程任务返回一个字符串
代码语言:javascript代码运行次数:0运行复制public class MyString implements Callable<String> {
@Override
public String call() throws Exception {
return "那一夜,你没有拒绝我,那一夜,你伤害了我";
}
}
代码语言:javascript代码运行次数:0运行复制public class MySum implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum+=i;
}
return sum;
}
}
代码语言:javascript代码运行次数:0运行复制public class Test01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池对象
ExecutorService es = (2);
Future<String> f1 = es.submit(new MyString());
Future<Integer> f2 = es.submit(new MySum());
println(f1.get());
println(f2.get());
}
}
第六章:定时器_Timer
Timer
类用于定时执行任务。
schedule(TimerTask task, Date firstTime, long period)
:安排任务在指定时间开始执行,每隔一定时间重复执行。
public class Demo01Timer {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
println("金莲对涛哥说:涛哥,快起床了~~~");
}
}, new Date(), 2000L);
}
}
小结
通过今天的学习,希望可以帮助你掌握多线程的高级应用,包括等待唤醒机制、Lock锁、Callable接口、线程池以及定时器的使用,这些都是Java并发编程中非常重要的知识点。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。 原始发表:2024-12-25,如有侵权请联系 cloudcommunity@tencent 删除对象多线程线程线程池public#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格
推荐阅读
留言与评论(共有 12 条评论) |
本站网友 wb主题 | 5分钟前 发表 |
本文参与 腾讯云自媒体同步曝光计划 | |
本站网友 扬州房地产网 | 2分钟前 发表 |
JVM自动执行run方法 | |
本站网友 软组织肿瘤 | 27分钟前 发表 |
Callable接口 | |
本站网友 上海宠物展 | 2分钟前 发表 |
本文参与 腾讯云自媒体同步曝光计划 | |
本站网友 看病咨询 | 3分钟前 发表 |
Lock锁 | |
本站网友 大连交易所 | 30分钟前 发表 |
setPriority(int n):设置线程优先级 | |
本站网友 治疗不育 | 16分钟前 发表 |
实现Runnable | |
本站网友 内存卡格式化工具 | 22分钟前 发表 |
所以需要用到同步代码块中 | |
本站网友 002526山东矿机 | 12分钟前 发表 |
代码语言:javascript代码运行次数:0运行复制public class Test01 { public static void main(String[] args) { BaoZiPu baoZiPu = new BaoZiPu(); Product product = new Product(baoZiPu); Cumer cumer = new Cumer(baoZiPu); new Thread(product).start(); new Thread(product).start(); new Thread(product).start(); new Thread(cumer).start(); new Thread(cumer).start(); new Thread(cumer).start(); } }代码语言:javascript代码运行次数:0运行复制/* count和flag可以定义成包装类 但是要记得给count和flag手动赋值 不然对于本案例来说 | |
本站网友 鱼腥草孕妇 | 25分钟前 发表 |
一个线程任务返回一个字符串代码语言:javascript代码运行次数:0运行复制public class MyString implements Callable<String> { @Override public String call() throws Exception { return "那一夜 | |
本站网友 卧城 | 2分钟前 发表 |
生产线程等待 if (baoZiPu.isFlag()==true){ try { baoZiPu.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } //2.如果flag为false |