并发:在一”段“时间内,很多事情同时发生(程序开发角度:程序运行期间,多个线程同时操作相同资源)
线程:内存分给进程一部分计算资源,“进程的每个线程在线程被分配的内存中在争夺计算资源”(线程优先级是概率问题)
- 创建线程的几种方式:
1.继承thread类,重写run方法(这个类和方法都不是抽象的)继承后子类成为了一个独立的线程
线程需要被启动(.start-是本地方法,启动"一个"线程(只有C语言可以操作CPU))调用run方法(直接调用run是普通的对象调方法)
2.实现Runnable接口(函数式接口)重写run方法,但是要使用Thread构造一个线程才能使用.start
3.(JDK1.5)实现callable接口<泛型接口> 重写 call() throws Exception 可以抛异常,有返回值,还要转成thread方法才能
start(1.先转成runnable-使用futureTask构造成runnable2.转成thread类,使用thread构造)
4.
- 线程的生命周期:
1.NEW 未被start调用执行(未加载)
2.Runnable 线程正在JVM被执行,等待操作系统调度(加载后待运行)
3Blocked 阻塞,因为某些原因不能立即执行(外部原因导致)
(os:等待就是被挂起了,就不在cpu中了)
4.Waiting 无限期等待(如果没被唤醒则一直等待)(等待一般是主动调用方法等待)
5.TimedWaiting 有限期等待(等一段时间后自己进入)
Terminated 终止线程的状态
- 守护线程(守护用户线程的线程)(程序的小模块):为用户线程提供服务,用户需要时就出现,不需要就不用出现——经常用于支持后台任务(大多数JVM线程都是守护线程) 线程.setDaemon(true)设置守护线程
*java内存模型:CPU-CPU寄存器-一级缓存-二级缓存-主内存 (线程和线程之间,共享数据的通信JAVA中都是必须要从主内存走一遍)
(安全问题-读数据时和向主内存中写数据产生的问题)-程序就是数据的读写操作;
1.(顺序性)指令重排(最大化的利用内存资源产生的结果):互相无关的指令之间计算机Cpu可以随机选择命令的执行顺序-或者说CPU用多个线程同时执行这些命令(volatile-内存屏障禁止重排序关键字可以解决)
2.(可见性)不可见性:是指线程之间的可见性,一个线程修改的数据(没写到主内存之前)对另一个线程是不可见的(volatile关键字可以解决)volatile关键字可以保证直接从主存中读取一个变量,如果这个变量被修改后,总是会被写回到主存中去;
3.(原子性)线程争抢:争抢计算资源,但都拿到了相同的资源进行操作(本来要操作两次,结果都对相同资源操作导致进行了同样的操作,相当于只操作了一次)加(synchronized同步)锁
- 线程安全的实现方法:
1.数据不可变:一切不可变的对象都是线程安全的 final;字符串
2.互斥同步:加锁(悲观锁)
3.非阻塞同步:{无锁变成}自旋
4.无同步方案:限制共享数据的可见范围,就是共享(数据)所有的操作一个
博文:https://zhuanlan.zhihu.com/p/29881777(谢谢作者大大)多看!!!!!!!
- sychronized:拿到共享对象当成锁(只对于当前虚拟机可以解决线程安全问题-不能跨JVM解决问题)就是把同步的代码变成单线程执行
同步方法
1:修饰实例方法:调用该方法的实例(this)-加锁对象-同步监视器是实例对象
2:修饰静态方法:类对象-加锁对象(同步代码块)实现runnable接口推荐使用
同步代码块:obj(或类对象)-加锁对象(同步代码块)实现runnable接口推荐使用
- 死锁:互相占着对方的锁不放手
线程重入:拿到锁后不会再被同一个锁阻碍(synchronized可重入锁)—锁相当于认证
- 为什么要使用wait(timed_wait)
cpu会根据计算情况挂起或者唤醒一些线程(在内核中操作,系统压力大),此时应用上的共享数据会处于锁定状态,我们为了不让CPu进行挂起,所以我们人为让一些线程"等待"进入,让CPU能够一直保持运行状态(不进行挂起操作-耗内核)
- 方法总结:
1.sleep会释放cpu资源,但不会释放锁
2.yield方法释放cpu执行权,但保留了CPU的执行资格
3.join抢夺执行权
4.wait释放资源,也释放锁
- sleep和wait的区别(面试题)
1.出处:join是Thread类提供的方法,wait是Object类提供的方法
2.锁的控制:sleep会释放cpu资源,但不会释放锁;wait释放
- 线程退出:推出标志
1-.stop
2-定一个volatile标记,通过标记来让线程确定是否执行
3-.interrupt 调用此方法会抛异常,线程会进入挂起状态
- ------------------------手动锁-实现synchroniazed---------------------------------能控制主动权
1.LockSuport 锁工具类
.park(阻塞线程)和.unpark(疏通线程)实现了wait和notify的功能:
但park不释放锁(所以不会出现死锁);中断不会出现interruptedException
2.Lock接口
.lock当前线程获得锁 .trylock查看是否获得锁 .unlock当前线程释放锁
reentrantLock-实现了lock接口
- lock和synchronized的区别
1.Lock是一个接口,synchronized是关键字,由c语言实现
2.synchronized发生异常时会释放锁,lock发生异常时若没有主动释放,会一直占着锁,所以一般在finally中释放
3.lock可以让等待锁的线程响应中断,使用synchronized只会让线程一直等待,不会中断
4.lock效率比synchronized效率高
lock子类—读写锁:ReentrantReadWriteLock读写锁保证了多个线程可以一起读数据,但是如果有个线程要写数据,其他线程就都不能读
(再读操作多,写操作少时)内部类:readlock-writelock只要writelock启动了其他readlock就不会获得锁了
- Atomic原子类,基本数据类型的包装类-保证我们的数据不出问题
- 常见的工作队列:ArrayBlockingQueue:基于数组的有界阻塞队列
LinkedBlockingQueue:基于链表的有界阻塞队列
- 线程池的拒绝策略:AbortPolicy:直接抛出异常,默认的策略
CallerRunPolicy:用调用者所在的线程执行任务
DiscardOldestPolicy:丢弃阻塞队列中存在时间最长的线程,并执行当前任务
DiscardPolicy:直接丢弃线程
Tips:对象监视器
点击查看多线程通信代码和注释
public class Test1 implements Runnable{
public static Object waitOBJ=new Object();
@Override
public void run() {
synchronized (waitOBJ){
System.out.println(Thread.currentThread().getName()+"拿到了锁");
try {
waitOBJ.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName()+"释放了锁");
}
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(new Test1(),"线程一");
thread.start();
Thread.sleep(1000);
synchronized (waitOBJ){
waitOBJ.notify();
System.out.println(Thread.currentThread().getName()+"正在运行");
}
}
}
点击查看多线程实现多窗口买票
package com.jr.Learn0813;
import org.testng.annotations.Test;
public class Test1 implements Runnable{
public static Object waitOBJ=new Object();
public static Integer num=100;
@Override
public void run() {
while (true){
synchronized (waitOBJ){
System.out.println(Thread.currentThread().getName()+"->"+num--);
try {
if (num>3){
waitOBJ.notifyAll();
waitOBJ.wait();
}else {
waitOBJ.notifyAll();
break;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(new Test1(),"线程一");
thread.start();
Thread thread1=new Thread(new Test1(),"线程二");
thread1.start();
Thread thread2=new Thread(new Test1(),"线程三");
thread2.start();
Thread thread3=new Thread(new Test1(),"线程四");
thread3.start();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现