复习JavaDay07
线程的5种状态
新生状态:Thread thread = new Thread();
就绪状态:当调用start()方法,线程立即进入就绪状态,但并不以为着立即调度执行
运行状态:进入运行状态,线程才真正执行线程体的代码块。
阻塞状态:当调用sleep(),wait或者同步锁时,线程进入阻塞状态,就是代码不往下执行
阻塞事件解除后,重新进入就绪状态,等待cpu调度执行。
死亡状态:(dead):线程中断或者结束,一旦进入死亡状态,就不能再次启动。
线程的常用方法
方法 | 说明 |
---|---|
setPriority(int newPriority); | 更改线程优先级 |
static void sleep(long millis); | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join(); | 等待该线程终止 |
static void yield(); | 暂停当前正在执行的线程对象,并执行其他线程 |
void intrrupt(); | 中断线程,别用这个方式 |
boolean isAlive(); | 测试线程是否处于活动状态 |
自定义标识位结束线程:案例
不推荐使用JDK提供的stop()和destroy(),推荐线程自己停下来,设置一个变量,例如flag = false,则终止线程。
//设置标识位让线程在指定次数停止
public class TestThreadStop implements Runnable {
private boolean flag = true;
@Override
public void run() {
int i = 0;
//这里执行循环
while (flag) {
System.out.println(Thread.currentThread().getName() + "运行了第" + i++ + "次");
}
}
public boolean stop() {
return this.flag = false;
}
public static void main(String[] args) {
//错误示例:原因,在调用sotp方法时就需要new,和现在这个对象不是同一对象了,将陷入死循环
//new Thread(new TestThreadWay(),"自定义线程").start();
TestThreadStop t = new TestThreadStop();
Thread thread = new Thread(t, "自定义线程");
thread.start();
//当main线程运行900次时结束掉自定义线程
for (int i = 0; i < 1000; i++) {
if (i == 900) {
System.out.println(Thread.currentThread().getName()+"线程结束了====================");
t.stop();
break;
}
System.out.println("main------=========" + i);
}
}
}
sleep实现计时器案例:
public class TestThreadSleep implements Runnable {
@Override
public void run() {
int time = 0;
while (true) {
try {
Thread.sleep(1000);
System.out.println(time++);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (time == 60) {
System.out.println("1分钟结束,计时停止");
break;
}
}
}
public static void main(String[] args) {
new Thread(new TestThreadSleep()).start();
}
}
join() 合并线程:举例线程插队
package com.softeem;
//合并线程 join()
public class TestThreadSleep implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Vip来了" + i);
}
package com.softeem;
//模拟三个人抢票,保证线程安全
public class Simulation {
public static void main(String[] args) {
//新建三条线程
TicketGrabbing t = new TicketGrabbing();
new Thread(t, "小明").start();
new Thread(t, "小红").start();
new Thread(t, "黄牛").start();
}
}
class TicketGrabbing implements Runnable {
//定义票的总数
private int icket = 1000;
//定义定制参数
private boolean flag = true;
@Override
public void run() {
while (flag) {
stop();
}
}
//停止线程方法:注意,锁要加在使用的同步数据中,否则会失效
public synchronized boolean stop() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (icket > 0) {
System.out.println(Thread.currentThread().getName() + "抢到了第" + icket);
icket--;
return flag = true;
} else {
return flag = false;
}
}
}
线程同步块案例
//把ArrayList改成线程安全的(使用线程同步块)
public class Damo {
public static void main(String[] args) {
//创建两根线程同时执行
NewTest newTest = new NewTest();
new Thread(newTest, "线程1").start();
new Thread(newTest, "线程2").start();
}
}
class NewTest implements Runnable {
//定义线程结束变量
private boolean flag = true;
//定义总执行次数
private int a = 10_0000;
//创建List数组对象
List list = new ArrayList<>();
@Override
public void run() {
while (flag) {
add();
}
}
public boolean add() {
//同步代码块,this为当前类
synchronized (this) {
//如果执行次数大于0就继续执行
if (a > 0) {
System.out.println(Thread.currentThread().getName() + "存入了" + a);
list.add(a);
a--;
return flag = true;
} else {
//否则就结束方法
System.out.println(list.size());
return flag = false;
}
}
}
}
死锁避免方法
产生死锁的四个必要条件:
1. 互条件:一个资源每次只能被一个进程使用
2 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放3 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺
4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
lock锁(推荐)
ReentrantLock可重入锁
package com.softeem;
import java.util.concurrent.locks.ReentrantLock;
//三个人抢票,使用Lock
public class TestLock {
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(t, "线程1").start();
new Thread(t, "线程2").start();
new Thread(t, "线程3").start();
}
}
class Ticket implements Runnable {
//定义票总数为100
private int ticket = 10;
//创建Lock锁对象
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
//lock锁在try,catch,finally中执行,最后需要释放锁资源
try {
//开启lock
lock.lock();
//睡眠1ms
Thread.sleep(500);
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "抢到了第" + ticket);
ticket--;
} else {
return;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭lock
lock.unlock();
}
}
}
}
synchronized与Lock的对比 Lock是显式锁 (手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁,出了 作用域自动释放 Lock只有代码块锁,synchronized有代码块锁和方法锁 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展 性 (提供更多的子类 优先使用顺序: Lock 同步代码块 (已经进入了方法体,分配了相应资源)->同步方法 (在方法体之外)
生产者与消费者之红绿灯标示法
package com.softeem;
//测试生产者和消费者,红绿灯法(演员:观众:产品)
public class TestPC2 {
public static void main(String[] args) {
//实例化TV类
TV tv = new TV();
//实例化演员类,参数为产品类
Player p = new Player(tv);
//实例化观众类,参数为产品类
Watcher w = new Watcher(tv);
new Thread(p).start();
new Thread(w).start();
}
}
//演员类
class Player implements Runnable {
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 implements Runnable {
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//这里需要判断逻辑,观众观看时演员等待,演员表演时观众等待,
//如果没有节目,需要演员先去表演了观众才可以观看
class TV {
//设置标志位,true时观众观看,false时演员表演
private boolean flag = true;
//节目
private String voice;
//演员表演方法及逻辑
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;
//标志位取反改为false,false时说明演员已经表演完,观众可以观看了
this.flag = !this.flag;
}
//观众观看方法及逻辑
public synchronized void watch() {
//如果标志位为真观众等待(演员表演),为假就执行观看
if (flag) {
try {
//演员表演,当前线程等待
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//观众观看,节目演员表演完传递过来的
System.out.println("观众观看了节目:" + voice);
//通知唤醒其他线程
this.notifyAll();
//标志位取反改为true
this.flag = !this.flag;
}
}
创建线程池
//创建线程池:参数为线程数量
ExecutorService service = Executors.newFixedThreadPool(10);
1.创建线程池,2.编写要执行的类,3.关闭线程链接。
package com.softeem;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//测试线程池
public class TestThreadPool {
public static void main(String[] args) {
//创建线程池:参数为线程数量
ExecutorService service = Executors.newFixedThreadPool(10);
//创建线程,参数为执行对象
service.execute(new Test());
service.execute(new Test());
service.execute(new Test());
//关闭线程链接
service.shutdown();
}
}
class Test implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
}
public static void main(String[] args) throws InterruptedException {
TestThreadSleep t = new TestThreadSleep();
Thread t1 = new Thread(t);
t1.start();
//join()方法实现线程插队,注意:执行较小任务时不适用,会存在争夺时间片问题提前插队
for (int i = 0; i < 500; i++) {
System.out.println("排队" + i);
if (i == 50) {
t1.join();
}
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~