多线程核心语法②

一、获取线程引用Thread.currentThread()

若使用继承Thread,直接使用this拿到线程实例;

若使用Runnable/lambda,不能使用this(不再指向Thread对象),只能Thread.cyrrentThread();

二、线程休眠 sleep()

sleep清空标志位,是为了提供更多的“可操作行空间”.

若sleep()休眠时间不到,就要终止线程。在catch中加入一些代码进行处理:

1.加break:让线程立即结束;

2.不加break:让线程不结束,继续执行;

3.写一些其他代码再break:让线程执行一些逻辑后再结束;

三、线程状态 getState()

1.NEW

Thread对象被创建好了,但是还没有调用strat方法在系统中创建线程。

  • 描述:线程被创建但尚未启动。

  • 触发条件:通过 new Thread() 创建线程对象,但尚未调用 start() 方法。

  • 示例

2.RUNNABLE

就绪状态,正在运行/随时可以去cpu上执行。

  • 描述:线程正在 JVM 中执行,但可能正在等待操作系统资源(如 CPU 时间片)。

  • 触发条件:调用 start() 方法后,线程进入 RUNNABLE 状态。

  • 注意

    • RUNNABLE 状态包括 就绪(Ready) 和 运行中(Running) 两种情况。

    • 线程是否真正运行取决于操作系统的调度。

  • 示例

3.BLOCKED

锁竞争引起的堵塞。

  • 描述:线程因为等待获取锁而进入阻塞状态。

  • 触发条件

    • 线程尝试进入一个被其他线程持有的同步代码块或方法。

    • 线程在等待进入 synchronized 块时。

  • 示例

4.WAITING

不带时间的堵塞,必须满足一定的条件才能解除堵塞。

  • 描述:线程无限期等待其他线程执行特定操作。

  • 触发条件

    • 调用 Object.wait() 方法。

    • 调用 Thread.join() 方法(不带超时参数)。

  • 示例

5.TIMED_WAITING

指定时间的堵塞,到达一定时间会自动解除堵塞。

  • 描述:线程在指定的时间内等待。

  • 触发条件

    • 调用 Thread.sleep(long millis) 方法。

    • 调用 Object.wait(long timeout) 方法。

    • 调用 Thread.join(long millis) 方法。

  • 示例

6.TERMINATED

Thread对象仍存在,但系统内部线程已经执行完毕。

  • 描述:线程执行完毕或异常退出。

  • 触发条件

    • 线程的 run() 方法执行完毕。

    • 线程抛出未捕获的异常。

  • 示例

7.关系图

四、线程安全问题

为了实现“并发编程”引入多线程。

某个代码,无论单线程执行/多线程下执行,均不产生bug=>“线程安全”

单线程下运行正确,多线程下产生bug=>“线程不安全”/“存在线程安全问题”

例子:count++

一个线程自增5w次,2个线程自增10w次,实际结果不符合预期=>"bug"

                    1.load        从内存中读取数据到cpu寄存器中         

count++  => 2.add         把寄存器的值+1

                    3.save        把寄存器的值写回到内存中

需要确保第一个线程save之后,第二个线程再load,否则会有bug。

实际还会存在,一个线程执行2/N次自增,被另一线程覆盖成自增一次的结果。

加锁是把count++这三步操作变成原子的。但在加锁后,执行三个操作过程中,线程仍会调度

但即使加锁的线程被调度走,其他线程也无法“插队执行”。

线程不安全的原因

1.根本原因

操作系统上的线程是“随机调度,抢占式执行” <=> 线程之间执行的顺序有很多变数

2.代码结构

代码中多个线程,同时修改同一个变量。

①一个线程修改一个变量(安全)

②多个线程读取一个变量(安全)->只读取,变量的内容固定不变

③多个线程修改不同变量(安全)->不同变量,彼此之间不会相互覆盖

3.直接原因

多线程修改操作,本身不是“原子的”

count++ => 多个cpu指令,一个线程执行这些指令,执行到一半,可能被调度走。

若每个cpu指令均为“原子的”要么不执行,要么执行完。

4.内存可见性问题

5.指令重排序问题

解决方法:

针对原因一:

无法做出任何改变

针对原因二:

分情况,有时代码结构可调整,有时不可调整。

针对原因三:synchronized

原子性

通过特殊手段,将三个指令打包到一起,成为“整体”。加锁,锁具有“互斥”,“排他”性。

加锁的目的:为了把三个操作,打包成一个原子的操作(两个线程是否是针对同一个对象加锁

进行加锁时,需要先准备好“锁对象”。加锁解锁操作,都是依托于这个“锁对象”来展开的。

如果一个线程针对一个对象加锁以后,其他线程也尝试对这个对象加锁。=>阻塞(BLOCKED)

一直阻塞到前一个线程释放锁为止。=>锁冲突/锁竞争

synchronized

是调用系统的api进行加锁,系统的api本质上是靠cpu的特定指令来完成加锁的。

任何一个Object都可作为锁对象。重要的是:两个线程之间是否使用同一个对象,如果是同一个对象,就会竞争,不是同一个就不会。

synchronized修饰普通方法 <=> 给this加锁(锁对象this)

synchronized修饰静态方法 <=> 给类对象加锁

注:如果synchronized加到static方法上 <=>给类对象加锁

①一个线程加锁,一个不加锁仍存在线程安全问题

②两个线程针对不同的对象加锁,仍会存在线程安全问题

③对加锁操作的一些理解

类对象

把count放到一个Test t对象中,通过上述add方法来修改,加锁的时候锁对象写作this

获取到Test的类对象。在一个Java进程中,一个类的类对象只有一个。

注:JVM执行.class时,要先把这个.class文件读取到内存中,才能执行(类加载

JVM中需要一些特定的数据结构,来表示加载好的这些数据结构。

使用类名.class就会得到这个类的类对象(每个类都有类对象

类对象中包含了这个类的各种信息:类名,属性,属性名,属性类型(private,public)方法,方法名,参数类型,注解,继承了哪个类,实现了哪些接口......

类对象是反射机制的依据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值