PCB状态字段细分,线程安全问题,加锁,synchronized

简介: PCB状态字段细分,线程安全问题,加锁,synchronized


补充:之前的线程休眠 sleep ,参数是以ms作为单位,但是sleep本身就存在一些误差。sleep(1000),不一定是精确在休眠1000ms(线程的调度,也是需要时间的)

sleep(1000)的意思是说该线程在1000ms之后,就快恢复成“就绪状态”,此时只是随时去cpu执行,但是并不是立刻马上就去执行

因为sleep的特性,也诞生了一个特殊的技巧,sleep(0)让线程放弃cpu,准备下一轮的调度(一般后台开发不太涉及到这个东西,yield方法效果是和sleep(0)一样


一、💛  

PCB的状态字段:

就绪状态阻塞状态 是系统设定的状态。Java把这两个状态进行了细分。

NEW:安排了工作,但是还没有执行(类创建好了,但是没开始start),如下图,他还没有开始start.

Runnable:可工作的,又分成(1)正在工作的(线程在cpu上运行) (2)线程在这里排队,随时可以进去cpu

TERMINSTED:终止状态

BLOCKED:因为锁产生的阻塞(一会讲解锁)

WAITING:因为调用wait产生阻塞(不带时间的)(以后讲解)

TIMED_WAITING:因为sleep产生阻塞(带时间的)

 

 


二、💜

线程安全问题(重点,考点)

线程不安全:是说单线程执行下没有问题,但是多个线程的执行下,就出现问题了。

bug:实际运行效果,和预期效果不一样就可以叫做bug

count++:本质是分成三个步骤

1.把内存的数据,加载到cpu的寄存器

2.把寄存器中的数据进行+1

3.把寄存器中的数据写入内存中

而上述操作在两个线程中可能有以下几个问题

原因就是抢占式执行的结果

 

bug1号

bug2号,这种当然也很典型, 此外这两个线程的调度顺序也是不确定的,两个操作相对顺序也有差异,那么排序情况就有了无数种情况。

1W的循环结果里,在多少次运行,多少次出现覆盖结果,导致运算的中间结果,也被覆盖了,结果一定小于1w,很多代码都涉及线程安全问题,不仅仅是count++。

线程安全的原因:

1.多个线程之间调度顺序是随机的,操作系统使用抢占式,执行的策略来调整线程。和单线程不同的是,多线程喜爱,代码的执行顺序,产生了更多变化,以往只需考虑一个代码在一个固定顺序下执行正确即可,现在要考虑的多线程下,N种执行顺序下,代码执行结果都要正确。(跑的快和跑的对,有时候就是会冲突)

2.多个线程同时修改同一个变量,容易产生线程安全问题(代码的结构)  

一个线程修改一个变量,多个线程,读取同一个变量,多个线程修改,多个变量都是ok的

3.进行的修改,不是“原子性的”(事务里面,看我之前的),如果修改操作按原子方式去完成,此时也不会有线程安全问题,

4.内存可见性,引起的线程安全问题

5.指令重排序,引起的线程安全问题

针对3号的(也是解决线程安全的最主要的操作)解决方法:加锁,相当于把一组操作包装成一个原子的操作(此处这里的原子,则是通过锁进行互斥,我这个线程工作的时候,其他线程无法进行操作。

Java引入了原子操作synchronized关键字(务必会读(辛可肉耐子)会写

写在方法前面(进入方法就进行加锁操作,出了方法就解锁)

synchronized:只要出了代码不管你是return 也好,抛异常也好,都会正常解锁,比一般的什么lock()方便,所以lock()这种东西也不多介绍。

 

class Count{
    public  int count=0;
    synchronized   public  void increase(){
        count++;
    }
}
public  class  Demo {
    public static void main(String[] args) throws InterruptedException {
        Count counter = new Count();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                counter.increase();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                counter.increase();
            }
        });
        t1.start();
        t2.start();
        t1.join();       //等待t1执行完毕,之前上一个没写join,但是就算写他也是错的
        t2.join();       //等待t2执行完毕,最后main线程
        System.out.println(counter.count);
    }
}

 

三、💚  

那么来了一个问题,既然要串行化并发执行,那么多线程有存在的必要吗(不还是一个线程完事,去另一个线程吗?

答:有必要,我们看上面那一串代码,我们能知道线程不是只干了count++这一件事啊,我们也有循环,循环不也是我们需要做的吗,for循环并没有加锁,不涉及线程安全问题,for循环操作的变量是栈上的局部变量,两个线程有两个独立的空间,

两个i不是同一个变量,两个线程改不同的变量,当然不需要加锁了!

因此,两个线程,一部分代码串行化执行,有一部分并发式执行,仍然比纯粹的串行效率要高。

synchronized:进行加锁解锁,其实是以对象展开的

⚠️:t1占一次换t2,再换就是t1,不是5000次一直都是t1啥的哈,两个线程是同时执行的

加锁的目的是为了互斥使用资源(互斥的修改变量)

💖💖💖重点:如果两个线程针对同一个对象加锁,就会出现锁竞争/锁冲突(即会有阻塞现象)

假如是不同对象则不存在锁竞争,也就不会阻塞,那么数据也会变得不可靠。

  public  void increase(){
        synchronized (this){
        count++;
    }
  synchronized public void increase(){
        count++}
  //这两个式子相同的意思,就是上面的可以更灵活一点,可以写不同对象
class Count{
    public  int count=0;
    private Object locker=new Object();         //另一个对象
    public  void increase() {
        synchronized (this) {
            count++;              
        }
    }
    public  void increase2(){
        synchronized (locker){                //另一个对象,不存在锁竞争
        count++;
            }
  }
}
public  class  Demo {
    public static void main(String[] args) throws InterruptedException {
        Count counter = new Count();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                counter.increase();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                counter.increase2();
            }
        });
        t1.start();
        t2.start();
        t1.join();       //等待t1执行完毕
        t2.join();       //等待t2执行完毕,最后main线程
        System.out.println(counter.count);
    }
}

像是上述代码,两个对象针对不同对象加锁,此时不存在阻塞等待,也就不会让程序按照串行的方式进行count++,也就仍然会有线程安全问题,既然不阻塞等待,那么也就是说会有刚才那种的覆盖情况


相关文章
|
16天前
|
并行计算 安全 Java
Python GIL(全局解释器锁)机制对多线程性能影响的深度分析
在Python开发中,GIL(全局解释器锁)一直备受关注。本文基于CPython解释器,探讨GIL的技术本质及其对程序性能的影响。GIL确保同一时刻只有一个线程执行代码,以保护内存管理的安全性,但也限制了多线程并行计算的效率。文章分析了GIL的必要性、局限性,并介绍了多进程、异步编程等替代方案。尽管Python 3.13计划移除GIL,但该特性至少要到2028年才会默认禁用,因此理解GIL仍至关重要。
83 16
Python GIL(全局解释器锁)机制对多线程性能影响的深度分析
|
2月前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
2月前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
73 3
|
3月前
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
54 6
|
2月前
|
Java 关系型数据库 MySQL
【JavaEE“多线程进阶”】——各种“锁”大总结
乐/悲观锁,轻/重量级锁,自旋锁,挂起等待锁,普通互斥锁,读写锁,公不公平锁,可不可重入锁,synchronized加锁三阶段过程,锁消除,锁粗化
|
3月前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
63 4
|
4月前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
【10月更文挑战第6天】在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
41 2
|
4月前
|
Java 应用服务中间件 测试技术
Java21虚拟线程:我的锁去哪儿了?
【10月更文挑战第8天】
79 0
|
4月前
|
安全 调度 数据安全/隐私保护
iOS线程锁
iOS线程锁
42 0
|
4月前
|
Java 编译器 程序员
【多线程】synchronized原理
【多线程】synchronized原理
76 0

相关实验场景

更多