java多线程笔记 b站狂神视频笔记
线程创建
Thread、Runnable、Callable
继承Thread类和 实现Runnable接口 为重点,实现Callable接口仅作了解
Thread
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
package com.company.dxc;
// 创建线程的方式:继承Thread类 、重写run()方法、 调用start开启线程
// 总结:线程开启不一定立即执行,由cpu进行调度执行
public class TestThread01 extends Thread {
@Override
public void run() {
// run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("正在执行线程----" + i);
}
}
public static void main(String[] args) {
// main主线程
// 创建一个线程对象
TestThread01 testThread01 = new TestThread01();
//调用start()方法开启线程
testThread01.start();
for (int i = 0; i < 2000; i++) {
System.out.println("正在执行主方法******" + i);
}
}
}
Runnable
- 定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
package com.company.dxc;
// 创建线程方式2
// 实现Runnable接口,重写run方法,执行线程需要丢入Runnable接口实现类 调用start执行
public class TestThread02 implements Runnable {
@Override
public void run() {
// run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("正在执行线程----" + i);
}
}
public static void main(String[] args) {
// main主线程
// 创建Runnable接口的实现类对象
TestThread01 testThread01 = new TestThread01();
// 创建线程对象,通过线程对象来开启我们的线程,代理
// Thread tr = new Thread(testThread01);
// tr.start();
new Thread(testThread01).start();
for (int i = 0; i < 2000; i++) {
System.out.println("正在执行主方法******" + i);
}
}
}
Thread与Runnable比较
继承Thread类 | 实现Runnable接口 |
---|---|
子类继承Thread类具备多线程能力 | 实现接口Runnable具有多线程能力 |
启动线程:子类对象.start(); | 启动线程:传入目标对象+Thread对象.start(); |
不建议使用:避免OOP单继承局限性 | 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用 |
例子1. 购买火车票
通过本例子发现并发问题
package com.company.dxc;
// 多个线程同时操作同一个对象
// 买火车票的例子
// 问题:多个线程同时对一个资源进行操作 解决并发问题
public class TestThread03 implements Runnable {
// 票数
private int ticketNums = 10;
@Override
public void run() {
while (true){
if(ticketNums <= 0) break;
// 模拟演示
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " --> 拿到了NO:" + ticketNums-- + "票" );
}
}
public static void main(String[] args) {
TestThread03 ts = new TestThread03();
new Thread(ts, "stu").start();
new Thread(ts, "teacher").start();
new Thread(ts, "gangster").start();
}
}
例子2. 龟兔赛跑
package com.company.dxc;
// 模拟龟兔赛跑
public class race implements Runnable {
// 胜利者
private static String winner;
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
// 模拟兔子休息
if (Thread.currentThread().getName().equals("rabbit") && i%10 == 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 判断比赛是否结束
boolean flag = gameOver(i);
// 如果比赛结束 停止程序
if (flag) {break;}
System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "steps");
}
}
//判断是否完成比赛
private boolean gameOver(int steps) {
// 判断是否有胜利者
if (winner != null) return true; // 已经存在胜利者
{
if (steps >= 100) {
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
race r1 = new race();
new Thread(r1, "rabbit").start();
new Thread(r1, "tortoise").start();
}
}
Callable(仅了解)
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务
ExecutorService ser = Executors.newFixedThreadPood(1);
- 提交执行
Future<Boolean> result = ser.submit(t1);
- 获取结果
boolean r1 = result.get();
- 关闭服务
ser.shutdownNow();
静态代理
个人结婚与婚庆公司的例子
package com.company.dxc;
// 静态代理模式:
// 真实对象和代理对象都要实现同一个接口
// 代理对象要代理真实角色
// 好处 :
// 1. 代理对象可以做很多真实对象做不了的事情
// 2. 真实对象专注做自己的事情
public class StaticProxy {
public static void main(String[] args) {
You you = new You(); // 真实对象
new Thread(() -> System.out.println("i love you")).start(); // lambda表达式
new WeddingCompany(new You()).HappyMarry();
// WeddingCompany weddingCompany = new WeddingCompany(you);
// weddingCompany.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("结婚之前,布置场所");
}
}
Lambda表达式
为什么要使用lambda表达式
- 避免匿名内部类定义过多
- 可以让代码看起来更简洁
- 去掉无意义代码,留下核心逻辑
注:只有一行代码的情况下才能简化成一行;前提是接口为函数式接口
package com.company.dxc;
/*
* 推导lambda表达式
*/
public class TestLambda {
// 3. 静态内部类
static class Like2 implements Ilike{
@Override
public void lambda() {
System.out.println("i like lambda2!");
}
}
public static void main(String[] args) {
Ilike like = new Like();
like.lambda();
like = new Like2();
like.lambda();
// 4. 局部内部类
class Like3 implements Ilike{
@Override
public void lambda() {
System.out.println("i like lambda3!");
}
}
like = new Like3();
like.lambda();
// 5. 匿名内部类,没有类的名称,必须借助接口或者父类
like = new Ilike() {
@Override
public void lambda() {
System.out.println("i like lambda4");
}
};
like.lambda();
// 6. 用lambda表达式
like = () -> {
System.out.println("i like lambda5");
};
like.lambda();
}
}
// 1. 定义一个函数式接口
interface Ilike{
void lambda();
}
// 2. 实现类
class Like implements Ilike{
@Override
public void lambda() {
System.out.println("i like lambda!");
}
}
线程状态
创建状态、阻塞状态、死亡装填、就绪状态运行状态
线程的方法
停止线程
- 不推荐使用JDK提供的
stop(); destroy();
方法 - 推荐线程自己停止
- 建议使用一个标志位进行终止变量
package com.company.dxc;
public class TestStop implements Runnable {
// 1. 设置标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println("Thread is running...." + i++);
}
}
// 2. 设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
// main 先执行
for (int i = 0; i < 1000; i++) {
System.out.println("main is running..." + i);
if(i == 900){
//调用stop方法停止线程
testStop.stop();
System.out.println("Thread stop!");
}
}
}
}
线程休眠
- sleep(时间)指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间到达后线程进入就绪状态
- sleep可以模拟网络延时,倒计时等
- 每一个对象都有一个锁,sleep不会释放锁
package com.company.dxc;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep {
public static void main(String[] args) throws InterruptedException {
// down();
// 打印当前系统时间
Date date = new Date(System.currentTimeMillis());
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date = new Date(System.currentTimeMillis()); // 更新当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void down() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(1000);
System.out.println(num --);
if(num <= 0) break;
}
}
}
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度,礼让不一定成功,看CPU心情
package com.company.dxc;
// 测试不一定成功的线程礼让
public class TestYield {
public static void main(String[] args) {
myYield myYield = new myYield();
new Thread(myYield, "a").start();
new Thread(myYield, "b").start();
}
}
class myYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
Join
- join合并线程,待此线程执行完成后再执行其他线程,其他线程阻塞
- 可以想象成插队
package com.company.dxc;
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("★ VIP Thread ☆" + i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
// 主线程
for (int i = 0; i < 500; i++) {
if(i == 200){
thread.join(); // 插队
}
System.out.println("main" + i);
}
}
}
线程优先级
- java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,调度器按照优先级决定应该调度哪个线程来执行
- 线程的优先级用数字表示,范围从1~10
Thread.MIN_PRIORITY = 1;
- 使用以下方式改变或获取优先级
getPriority().setPriority(int xxx);
优先级低只是意味着获得调度的概率低,并不是高优先级必然先调用 (性能倒置问题)
package com.company.dxc;
public class TestPriority extends Thread {
public static void main(String[] args) {
// 主线程优先级
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
myPriority myPriority = new myPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
Thread t5 = new Thread(myPriority);
// 先设置优先级 再启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY); // Max 为最大 10
t4.start();
t5.setPriority(Thread.MIN_PRIORITY); // min 为最小 1
t5.start();
}
}
class myPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
}
}
守护线程(daemon)
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 如:后台记录操作日志、监控内存、垃圾回收 etc.
package com.company.dxc;
// 测试守护线程 上帝守护人类为例
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
Human human = new Human();
Thread thread = new Thread(god);
thread.setDaemon(true); // 默认false表示用户线程,正常线程都是用户线程
thread.start();// 守护线程启动
new Thread(human).start(); // 用户线程启动
}
}
// 上帝
class God implements Runnable{
@Override
public void run() {
while(true){ // 按理来说不会结束 但作为守护线程在用户线程结束后 随之结束(可能会伴随虚拟机关闭的一点点延迟)
System.out.println("正在守护!");
}
}
}
// 人类
class Human implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("每天都要开心活着!");
}
System.out.println("goodbye world!");
}
}
线程同步
并发:同一个对象被多个线程同时操作
处理多线程问题时,多线程访问一个对象,并且某些线程还想修改这个对象,这时候就需要线程同步。线程同步是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕再让下一个线程使用
队列与锁
两个不安全线程程序的例子
1. 不安全购票
package com.company.dxc;
import javax.swing.plaf.TableHeaderUI;
//不安全买票
// 线程不安全,有负数
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station, "买票人老壹 ").start();
new Thread(station, "买票人老贰 ").start();
new Thread(station, "黄牛 ").start();
}
}
class BuyTicket implements Runnable{
// 票
private int ticketNum = 10;
boolean flag = true; // 外部停止方式
@Override
public void run() {
// 买票
while(true){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
// 判断是否有票
if(ticketNum <= 0){
flag = false;
return;
}
//模拟延时
Thread.sleep(100);
// 买票
System.out.println(Thread.currentThread().getName() + "买到了" + ticketNum--);
}
}
2. 不安全取款
package com.company.dxc;
// 不安全取钱
// 两个人去取钱
public class UnsafeBank {
public static void main(String[] args) {
// 账户
Account account = new Account(100, "存款金额");
Drawing you = new Drawing(account, 50, "你");
Drawing gf = new Drawing(account, 100, "对方");
you.start();
gf.start();
}
}
// 账户
class Account{
int money; // 余额
String name; // 卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
// 银行 模拟取款
class Drawing extends Thread{
Account account; // 账户
int drawingMoney; // 取了多少钱
int nowMoney; // 还剩多少钱
public Drawing(Account account, int drawingMoney, String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//取钱操作
@Override
public void run() {
// 判断有没有钱
if(account.money - drawingMoney < 0){
System.out.println(Thread.currentThread().getName() + "钱不够,取不了咯!");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 卡内余额 = 余额 - 你取的钱
account.money = account.money - drawingMoney;
// 你手里的钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "余额为:" + account.money);
// 此时 Thread.currentThread().getName() = this.getName()
System.out.println(this.getName() + "手里的钱:" + nowMoney);
}
}
同步方法
-
由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:
synchronized
方法和synchronized
块同步方法
public synchronized void method(int args){ }
-
synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行
package com.company.dxc;
import javax.swing.plaf.TableHeaderUI;
//不安全买票
// 线程不安全,有负数
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station, "买票人老壹 ").start();
new Thread(station, "买票人老贰 ").start();
new Thread(station, "黄牛 ").start();
}
}
class BuyTicket implements Runnable{
// 票
private int ticketNum = 10;
boolean flag = true; // 外部停止方式
@Override
public void run() {
// 买票
while(true){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// *********此处进行同步方法************
// synchronized 同步方法,锁的是this
private synchronized void buy() throws InterruptedException {
// 判断是否有票
if(ticketNum <= 0){
flag = false;
return;
}
//模拟延时
Thread.sleep(100);
// 买票
System.out.println(Thread.currentThread().getName() + "买到了" + ticketNum--);
}
}
package com.company.dxc;
// 不安全取钱
// 两个人去取钱
public class UnsafeBank {
public static void main(String[] args) {
// 账户
Account account = new Account(100, "存款金额");
Drawing you = new Drawing(account, 50, "你");
Drawing gf = new Drawing(account, 100, "对方");
you.start();
gf.start();
}
}
// 账户
class Account{
int money; // 余额
String name; // 卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
// 银行 模拟取款
class Drawing extends Thread{
Account account; // 账户
int drawingMoney; // 取了多少钱
int nowMoney; // 还剩多少钱
public Drawing(Account account, int drawingMoney, String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//取钱操作
@Override
public void run() {
// ----------此时进行同步操作--------------
// synchronized 默认锁的是this,所以此时用同步块对account进行同步
synchronized (account){
// 判断有没有钱
if(account.money - drawingMoney < 0){
System.out.println(Thread.currentThread().getName() + "钱不够,取不了咯!");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 卡内余额 = 余额 - 你取的钱
account.money = account.money - drawingMoney;
// 你手里的钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "余额为:" + account.money);
// 此时 Thread.currentThread().getName() = this.getName()
System.out.println(this.getName() + "手里的钱:" + nowMoney);
}
}
}
死锁
产生死锁的四个必要条件
- 互斥:一个资源每次只能被一个进程使用
- 请求与保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺:进程已获得的资源,在未用完之前,不能强行剥夺
- 循环等待:若干进程之间形成一种头尾相接的循环等待资源关系
package com.company.dxc;
// 死锁:多个线程互相拥有对方需要的资源,形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup moore = new Makeup(0, "Moore");
Makeup dove = new Makeup(0, "Dove");
moore.start();
dove.start();
}
}
class Lipstick{
}
class Mirror{
}
class Makeup extends Thread{
// 需要的资源只有一份,用static来保证只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice; // 选择
String girlName; // 使用化妆品的人
Makeup(int choice, String girlName){
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
// 化妆
try {
Makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 化妆 互相持有对方的锁,就是需要拿到对方的资源
private void Makeup() throws InterruptedException {
if(choice == 0){
synchronized (lipstick){
// 获得口红的锁
System.out.println(this.girlName + "获得口红的锁");
Thread.sleep(1000);
}
synchronized (mirror){
// 获得镜子的锁
System.out.println(this.girlName + "获得镜子的锁");
}
}else{
synchronized (mirror){
// 获得口红的锁
System.out.println(this.girlName + "获得镜子的锁");
Thread.sleep(2000);
}
synchronized (lipstick){
// 获得镜子的锁
System.out.println(this.girlName + "获得口红的锁");
}
}
}
}
Lock 锁
class A{
private final ReentrantLock lock = new ReenTrantLock();
public void m(){
lock.lock();
try{
// 保证线程安全的代码
}
finally{
lock.unlock();
// 如果同步代码有异常,要将unlock()写入finally语句块
}
}
}
synchronized 与 lock 的对比
- Lock是显示锁,需要手动开启和关闭,synchronized为隐式锁,出了作用域自动释放
- lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用lock锁,jvm将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性
- 优先使用顺序
- Lock > 同步代码快(已经进入方法体,分配了相应资源)> 同步方法(在方法体之外)
线程协作 - 生产者消费者问题
线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
- 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,有需要马上通知消费者消费
- 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费
- 在生产者消费者问题中,仅有synchronized是不够的
- synchronized可组织并发更新同一个共享资源,实现了同步
- synchronized不能用来实现不同线程之间的消息传递(通信)
Java提供了几个方法解决线程之间的通信问题
方法名 | 作用 |
---|---|
wait() | 表示线程一直邓艾,直到其他线程通知,与sleep()不同,会释放锁 |
wait(long timeout) | 指定等待的毫秒数 |
notify() | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度 |
注意: 均是Object类的方法,都只能在同步方法或者同步代码快中使用,否则会抛出异常 IllegalMonitorStateException
管程法
- 生产者:负责生产数据的模块(可能是方法、对象、线程、进程)
- 消费者:负责处理数据的模块(可能是方法、对象、线程、进程)
- 缓冲区:消费者不能直接使用生产者的数据,利用中间“缓冲区”
package com.company.dxc;
// 测试 生产者消费者模型 --> 利用缓冲区解决:管程法
public class TestPC {
public static void main(String[] args) {
SynBuffer synBuffer = new SynBuffer();
new Producer(synBuffer).start();
new Consumer(synBuffer).start();
}
}
// 生产者
class Producer extends Thread{
SynBuffer buffer;
public Producer(SynBuffer buffer){
this.buffer = buffer;
}
// 生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("生产了" + i +"只鸡");
try {
buffer.push(new Chicken(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 消费者
class Consumer extends Thread{
SynBuffer buffer;
public Consumer(SynBuffer buffer){
this.buffer = buffer;
}
// 消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("消费了-->" + buffer.pop().id +"只鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 产品
class Chicken{
int id; // 产品编号
public Chicken(int id) {
this.id = id;
}
}
// 缓冲区
class SynBuffer{
//容器大小
Chicken[] chickens = new Chicken[10];
// 容器计数器
int count = 0;
// 生产者放入产品
public synchronized void push(Chicken chicken) throws InterruptedException {
// 如果容器满了,需要等待消费者消费
if(count == chickens.length){
// 通知消费者消费,生产等待
this.wait();
}
// 如果没有满,需要丢入产品
chickens[count] = chicken;
count ++;
// 可以通知消费者消费了
this.notifyAll();
}
// 消费者消费产品
public synchronized Chicken pop() throws InterruptedException {
// 判断能否消费
if(count == 0){
/// 等待生产者生产,消费者等待
this.wait();
}
// 如果可以消费
count --;
Chicken chicken = chickens[count];
// 吃完了,通知生产者生产
this.notifyAll();
return chicken;
}
}
信号灯法
package com.company.dxc;
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
// 生产者 --> 演员
class Player extends Thread{
TV tv;
public Player(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0){
this.tv.play("节目一:新闻联播");
}else{
this.tv.play("节目二:法治在线");
}
}
}
}
// 消费者 --> 观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv){
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
// 产品 --> 节目
class TV{
// 演员表演,观众等待 T
// 观众观看,演员等待 F
String voice; // 表演的节目
boolean flag = true;
// 表演
public synchronized void play(String voice){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:" + voice);
// 通知观众观看
this.notifyAll(); // 通知唤醒
this.voice = voice;
this.flag = !this.flag;
}
// 观看
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了:" + voice);
// 通知演员表演
this.notifyAll();
this.flag = !this.flag;
}
}
线程池
使用线程池
package com.company.dxc;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
// 1. 创建服务,创建线程池
// newFixedThreadPool 参数为线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
// 执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
// 2. 关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
总结
package com.company.dxc;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class summary {
public static void main(String[] args) throws ExecutionException, InterruptedException {
new MyThread1().start();
new Thread(new MyThread2()).start();
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();
Integer integer = futureTask.get();
System.out.println(integer);
}
}
// 1. 继承Thread类
class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("My Thread1");
}
}
// 2. 实现Runnable接口
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println("My Thread2");
}
}
// 3. 实现Callable接口
class MyThread3 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("My Thread3");
return 100;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义