Java 多线程一篇解决你的安全问题

Java多线程下保证线程安全的方案(单例模式、双重锁、LocalThread、@Scope) 多请求并发访问该Controller时,Tomcat会为每个请求开一个线程,每个线程都会从容器中获取到一个新的Controller对象,获取新的Controller对象时,必然会初始化其成员变量,其成员变量Service由于是Singleton的,并不会在自动注入时新建一个Service对象,也就是说,,属性隔离需要额外的配置,并且即便是做好隔离配置后,如果对该属性使用不当,也会造成属性作用范围扩大(或失效),这种线程安全问题,需要开发人员在开发时就额外注意,这种情况很难测试,且发现问题也很难排查。 阅读详情

前言
一个程序在运行起来的时候会转换成进程,通常含有多个线程。通常情况下,一个进程中的比较耗时的操作(如长循环、文件上传下载、网络资源获取等),往往会采用多线程来解决。

比如显示生活中,银行取钱问题、火车票多个售票窗口的问题,通常会涉及到并发的问题,从而需要多线程的技术。

当进程中有多个并发线程进入一个重要数据的代码块时,在修改数据的过程中,很有可能引发线程安全问题,从而造成数据异常。例如,正常逻辑下,同一个编号的火车票只能售出一次,却由于线程安全问题而被多次售出,从而引起实际业务异常。

一般我们常说某某类是线程安全的,某某是非线程安全的。其实线程安全并不是一个“非黑即白”单项选择题。按照“线程安全”的安全程度由强到弱来排序,我们可以将java语言中各种操作共享的数据分为以下5类:

  1. 不可变
    在java语言中,不可变的对象一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要再采取任何的线程安全保障措施。如final关键字修饰的数据不可修改,可靠性最高
    绝对线程安全
    绝对的线程安全完全满足Brian GoetZ给出的线程安全的定义,这个定义其实是很严格的,一个类要达到“不管运行时环境如何,调用者都不需要任何额外的同步措施”通常需要付出很大的代价。
  2. 相对线程安全
    相对线程安全就是我们通常意义上所讲的一个类是“线程安全”的。它需要保证对这个对象单独的操作是线程安全的,我们在调用的时候不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。
    在java语言中,大部分的线程安全类都属于相对线程安全的,例如Vector、HashTable、Collections的synchronizedCollection()方法保证的集合。
  3. 线程兼容
    线程兼容就是我们通常意义上所讲的一个类不是线程安全的。
    线程兼容是指对象本身并不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境下可以安全地使用。Java API中大部分的类都是属于线程兼容的。如与前面的Vector和HashTable相对应的集合类ArrayList和HashMap等。
  4. 线程对立
    线程对立是指无论调用端是否采取了同步错误,都无法在多线程环境中并发使用的代码。由于java语言天生就具有多线程特性,线程对立这种排斥多线程的代码是很少出现的。
    一个线程对立的例子是Thread类的supend()和resume()方法。如果有两个线程同时持有一个线程对象,一个尝试去中断线程,另一个尝试去恢复线程,如果并发进行的话,无论调用时是否进行了同步,目标线程都有死锁风险。正因此如此,这两个方法已经被废弃啦。
    提示:以下是本篇文章正文内容,下面案例可供参考
    第一种实现线程安全的方式
     同步代码块
public class ThreadSynchronizedSecurity {
 
    static int tickets = 15;
 
    class SellTickets implements Runnable {
        @Override
        public void run() {
            while (tickets > 0) {
                // 同步代码块
                synchronized (this) {
                    if (tickets <= 0) {
                        return;
                    }
                    System.out.println(Thread.currentThread().getName() + "--->售出第:  " + tickets + " 票");
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    tickets--;
                }
                if (tickets <= 0) {
                    System.out.println(Thread.currentThread().getName() + "--->售票结束!");
                }
            }
        }
    }
 
    public static void main(String[] args) {
        SellTickets sell = new ThreadSynchronizedSecurity().new SellTickets();
        Thread thread1 = new Thread(sell, "1号窗口");
        Thread thread2 = new Thread(sell, "2号窗口");
        Thread thread3 = new Thread(sell, "3号窗口");
        Thread thread4 = new Thread(sell, "4号窗口");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

备注:

在使用synchronized 代码块时,可以与wait()、notify()、nitifyAll()一起使用,从而进一步实现线程的通信。其中wait()方法会释放占有的对象锁,当前线程进入等待池,释放cpu,而其他正在等待的线程即可抢占此锁,获得锁的线程即可运行程序;线程的sleep()方法则表示,当前线程会休眠一段时间,休眠期间,会暂时释放cpu,但并不释放对象锁,也就是说,在休眠期间,其他线程依然无法进入被同步保护的代码内部,当前线程休眠结束时,会重新获得cpu执行权,从而执行被同步保护的代码。wait()和sleep()最大的不同在于wait()会释放对象锁,而sleep()不会释放对象锁。

notify()方法会唤醒因为调用对象的wait()而处于等待状态的线程,从而使得该线程有机会获取对象锁。调用notify()后,当前线程并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,才会释放对象锁。JVM会在等待的线程中调度一个线程去获得对象锁,执行代码。

需要注意的是,wait()和notify()必须在synchronized代码块中调用。notifyAll()是唤醒所有等待的线程

接下来,我们通过下一个程序,使得两个线程交替打印“A”和“B”各10次。请见下述实例代码:

package com.my.annotate.thread;
 
public class ThreadDemo {
 
    static final Object obj = new Object();
 
    //第一个子线程
    static class ThreadA implements Runnable {
        @Override
        public void run() {
            int count = 5;
            while (count > 0) {
                synchronized (ThreadDemo.obj) {
                    System.out.println("A-----" + count);
                    count--;
                    synchronized (ThreadDemo.obj) {
                        //notify()方法会唤醒因为调用对象的wait()而处于等待状态的线程,从而使得该线程有机会获取对象锁。
                        //调用notify()后,当前线程并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,
                        ThreadDemo.obj.notify();
                        try {
                            ThreadDemo.obj.wait();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
 
                    }
                }
            }
        }
 
    }
 
    static class ThreadB implements Runnable {
        @Override
        public void run() {
            int count = 5;
            while (count > 0) {
                synchronized (ThreadDemo.obj) {
                    System.out.println("B-----" + count);
                    count--;
                    synchronized (ThreadDemo.obj) {
                        //notify()方法会唤醒因为调用对象的wait()而处于等待状态的线程,从而使得该线程有机会获取对象锁。
                        //调用notify()后,当前线程并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,
                        ThreadDemo.obj.notify();
                        try {
                            ThreadDemo.obj.wait();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
 
    public static void main(String[] args) {
        new Thread(new ThreadB()).start();
     new Thread(new ThreadA()).start();
 
    }
 
}

第二种实现线程安全的方式
 同步方法

package com.my.annotate.thread;
 
public class ThreadSynchroniazedMethodSecurity {
    static int tickets = 15;
 
    class SellTickets implements Runnable {
        @Override
        public void run() {
            //同步方法
            while (tickets > 0) {
                synMethod();
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                if (tickets <= 0) {
                    System.out.println(Thread.currentThread().getName() + "--->售票结束");
                }
            }
        }
 
        synchronized void synMethod() {
            synchronized (this) {
                if (tickets <= 0) {
                    return;
                }
                System.out.println(Thread.currentThread().getName() + "---->售出第 " + tickets + " 票 ");
                tickets--;
            }
        }
    }
 
    public static void main(String[] args) {
        SellTickets sell = new ThreadSynchroniazedMethodSecurity().new SellTickets();
        Thread thread1 = new Thread(sell, "1号窗口");
        Thread thread2 = new Thread(sell, "2号窗口");
        Thread thread3 = new Thread(sell, "3号窗口");
        Thread thread4 = new Thread(sell, "4号窗口");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

第三种实现线程安全的方式
Lock锁机制, 通过创建Lock对象,采用lock()加锁,unlock()解锁,来保护指定的代码块

package com.my.annotate.thread;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class ThreadLockSecurity {
    static int tickets = 15;
 
    class SellTickets implements Runnable {
        Lock lock = new ReentrantLock();
        @Override
        public void run() {
            // Lock锁机制
            while (tickets > 0) {
                try {
                    lock.lock();
                    if (tickets <= 0) {
                        return;
                    }
                    System.out.println(Thread.currentThread().getName() + "--->售出第:  " + tickets + " 票");
                    tickets--;
                } catch (Exception e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                } finally {
                    lock.unlock();
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            if (tickets <= 0) {
                System.out.println(Thread.currentThread().getName() + "--->售票结束!");
            }
 
        }
    }
 
 
    public static void main(String[] args) {
        SellTickets sell = new ThreadLockSecurity().new SellTickets();
        Thread thread1 = new Thread(sell, "1号窗口");
        Thread thread2 = new Thread(sell, "2号窗口");
        Thread thread3 = new Thread(sell, "3号窗口");
        Thread thread4 = new Thread(sell, "4号窗口");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
 
}

由于synchronized是在JVM层面实现的,因此系统可以监控锁的释放与否;而ReentrantLock是使用代码实现的,系统无法自动释放锁,需要在代码中的finally子句中显式释放锁lock.unlock()。另外,在并发量比较小的情况下,使用synchronized是个不错的选择;但是在并发量比较高的情况下,其性能下降会很严重,此时ReentrantLock是个不错的方案。

二、线程安全的实现方法
保证线程安全以是否需要同步手段分类,分为同步方案和无需同步方案。

   1、互斥同步
    互斥同步是最常见的一种并发正确性保障手段。同步是指在多线程并发访问共享数据时,保证共享数据在同一时刻只被一个线程使用(同一时刻,只有一个线程在操作共享数据)。而互斥是实现同步的一种手段,临界区、互斥量和信号量都是主要的互斥实现方式。因此,在这4个字里面,互斥是因,同步是果;互斥是方法,同步是目的。

   在java中,最基本的互斥同步手段就是synchronized关键字,synchronized关键字编译之后,会在同步块的前后分别形成monitorenter和monitorexit这两个字节码质量,这两个字节码指令都需要一个reference类型的参数来指明要锁定和解锁的对象。

    此外,ReentrantLock也是通过互斥来实现同步。在基本用法上,ReentrantLock与synchronized很相似,他们都具备一样的线程重入特性。

   互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,因此这种同步也成为阻塞同步。从处理问题的方式上说,互斥同步属于一种悲观的并发策略,总是认为只要不去做正确地同步措施(例如加锁),那就肯定会出现问题,无论共享数据是否真的会出现竞争,它都要进行加锁。

   2、非阻塞同步 
   随着硬件指令集的发展,出现了基于冲突检测的乐观并发策略,通俗地说,就是先进行操作,如果没有其他线程争用共享数据,那操作就成功了;如果共享数据有争用,产生了冲突,那就再采用其他的补偿措施。(最常见的补偿错误就是不断地重试,直到成功为止),这种乐观的并发策略的许多实现都不需要把线程挂起,因此这种同步操作称为非阻塞同步。

    非阻塞的实现CAS(compareandswap):CAS指令需要有3个操作数,分别是内存地址(在java中理解为变量的内存地址,用V表示)、旧的预期值(用A表示)和新值(用B表示)。CAS指令执行时,CAS指令指令时,当且仅当V处的值符合旧预期值A时,处理器用B更新V处的值,否则它就不执行更新,但是无论是否更新了V处的值,都会返回V的旧值,上述的处理过程是一个原子操作。

    CAS缺点:

   ABA问题:因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

    ABA问题的解决思路就是使用版本号。在变量前面追加版本号,每次变量更新的时候把版本号加一,那么A-B-A就变成了1A-2B-3C。JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

   3、无需同步方案
    要保证线程安全,并不是一定就要进行同步,两者没有因果关系。同步只是保证共享数据争用时的正确性的手段,如果一个方法本来就不涉及共享数据,那它自然就无需任何同步操作去保证正确性,因此会有一些代码天生就是线程安全的。

    1)可重入代码
   可重入代码(ReentrantCode)也称为纯代码(Pure Code),可以在代码执行的任何时刻中断它,转而去执行另外一段代码,而在控制权返回后,原来的程序不会出现任何错误。所有的可重入代码都是线程安全的,但是并非所有的线程安全的代码都是可重入的。

   可重入代码的特点是不依赖存储在堆上的数据和公用的系统资源、用到的状态量都是由参数中传入、不调用 非可重入的方法等。

   (类比:synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时时可以再次得到该对象的锁)

  2)线程本地存储
   如果一段代码中所需的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行?如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内。这样无需同步也能保证线程之间不出现数据的争用问题。

   符合这种特点的应用并不少见,大部分使用消费队列的架构模式(如“生产者-消费者”模式)都会将产品的消费过程尽量在一个线程中消费完。其中最重要的一个应用实例就是经典的Web交互模型中的“一个请求对应一个服务器线程(Thread-per-Request)”的处理方式,这种处理方式的广泛应用使得很多Web服务器应用都可以使用线程本地存储来解决线程安全问题。

三、线程的生命周期以及五种基本状态

Java线程具有五中基本状态:
新建状态(New):当线程对象创建后就是进入到了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法,线程即进入到了就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是执行了start()此线程就会执行。
运行状态(Running):当CPU调度处于就绪状态的线程的时候,此时线程才会得以真正的执行,即进入运行状态。
注:就绪状态是进入运行状态的唯一入口,也就是说线程进入运行状态的前提是已经进入到了就绪状态。
阻塞状态(Blocked):处于运行状态的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,知道进入到就绪状态,才有机会再次被CPU调用以进入到运行状态,根据产生阻塞状态的三原因,阻塞状态可以分为三种:
等待阻塞–》运行状态的线程执行wait()方法,使线程进入到阻塞状态
同步阻塞–》线程获取同步锁失败,因为同步锁被其他线程所占用,这时线程就会进入同步阻塞状态;
其他阻塞–》通过调用线程的sleep()或join()或发出了I/O请求的时候线程会进入阻塞状态,当sleep()状态超时,join()等待线程终止或者超时,或者I/O处理完毕,线程就会重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因一场退出了run()方法,该线程就结束了生命周期。

参考代码见Gitee

保证线程安全的10个小技巧分享 对于从事后端开发的同学来说,线程安全问题是我们每天都需要考虑的问题线程安全问题通俗的讲:主要是在多线程的环境下,不同线程同时读和写公共资源(临界资源),导致的数据异常问题。比如:变量a=0,线程1给该变量+1,线程2也给该变量+1。此时,线程3获取a的值有可能不是2,而是1。线程3这不就获取了错误的数据?线程安全问题会直接导致数据异常,从而影响业务功能的正常使用,所以这个问题还是非常严重的。那么,如何解决线程安全问题呢?今天跟大家一起聊聊,保证线程安全的10个小技巧,希望对你有所帮助。 阅读详情

相关推荐

Java多线程--线程的安全问题与线程的同步机制介绍

Java多线程--线程的安全问题与线程的同步机制介绍

人生得意须尽欢 1198

直流电机静音控制方案:TB9051FTG与R7FA4M1AB3CFM应用

直流电机控制技术在现代医疗设备和智能家居中扮演着关键角色,其核心挑战在于如何有效降低PWM调速产生的电磁噪声。通过电流斜率控制和自适应死区技术等硬件优化,结合动态PWM频率调节算法,可以实现电机的高效静音运行。TB9051FTG驱动芯片与R7FA4M1AB3CFM微控制器的组合方案,特别适用于对噪声敏感的应用场景,如医疗输液泵和智能窗帘。该方案不仅提升了用户体验,还通过同步整流技术和双闭环控制算法,显著提高了系统效率和稳定性。

weixin_30600197的博客 401

线性代数-第24篇:线性代数在控制系统中的核心应用

线性代数通过状态空间建模、矩阵分解与优化算法,为控制系统提供了从理论分析到工程实现的完整框架。从传统PID控制到现代智能控制,其应用贯穿系统设计、分析与优化的全流程。未来,随着多智能体协同控制、量子控制系统等前沿领域的发展,线性代数将在高维矩阵计算、分布式优化等方向持续创新,为复杂控制系统的突破提供核心支撑。如果对线性代数在控制系统中的应用细节有疑问,欢迎在评论区留言!

383

Java多线程线程安全问题

Java多线程中的线程安全问题, 解决方法, 线程安全和不安全的标准类.

trong_ 的烂笔头 7212

线程安全问题?怎么解决线程安全

一、线程安全等级 之前的博客中已有所提及“线程安全问题,一般我们常说某某类是线程安全的,某某是非线程安全的。其实线程安全并不是一个“非黑即白”单项选择题。按照“线程安全”的安全程度由强到弱来排序,我们可以将java语言中各种操作共享的数据分为以下5类:不可变、绝对线程安全、相...

chushouxiu0552的博客 1679

面试官:说下单例模式以及在多线程下如何解决安全问题—(阿里淘系Java二面凉经)

阿里面试题: Spring AOP、DI Spring拦截器的原理 Spring中常见的设计模式 单例模式的实现 单例模式在多线程中实现时Volatile如何解决线程安全问题 如何更改Spring中Bean的作用范围 Redis数据库底层数据结构 跳表 红黑树 Redis为什么速度快 Redis IO多路复用是什么 MySQL数据库结构 MySQL数据库索引 MySQL..

AI乔治 155

Java中的线程安全问题(如果想知道Java中有关线程安全问题的基本知识,那么只看这一篇就足够了!)

Java中的线程安全问题(如果想知道Java中有关线程安全问题的基本知识,那么只看这一篇就足够了!)

秋刀鱼不做梦的编程日常 9866

Java多线程【状态与安全问题

1.线程是抢占行执行,线程充满了随机性[这就是线程不安全的万恶之源!!!]2.多线程对同一个变量进行修改操作3.针对的变量操作不是原子性的4.内存可见性被编译器优化5.重拍序这样问题所围绕的解决方法就是加锁!!在进行搭配volatile,wait,notify来进行解决问题和优化。

积跬步以至千里 2718

JAVA中的多线程安全问题解决方案

线程安全是指在多线程环境下,对共享资源进行并发访问时,程序能够正确地处理,不会出现数据不一致、逻辑错误等问题,确保程序的执行结果与单线程环境下的执行结果相同,或者符合预期的并发逻辑。有些代码在多线程环境执行下会出现问题,这样的问题就称为线程不安全原子性指的是一个操作是不可中断的,要么全部执行,要么都不执行。在多线程环境下,如果多个线程同时修改共享变量,可能会导致数据不一致。比如 i++ 这样的操作,虽然看起来是一条语句,但实际上分为读取、增加和写入三个步骤,这就不是原子的。

weixin_65528013的博客 1689

多线程编程的安全问题及其解决方法

经过了专栏的前几期的讲解,想必大家已经对多线程有了一些概念了,很多朋友一定已经想要进行多线程编程了。所以,我们开始第一步,运行上述代码,可以得到如下结果:大家的结果不一定是这个数字,但不会是正常应该得到的 1_000_000。那么这是为什么呢?为了更好的帮助大家进行理解,我要介绍关于的概念。表示一个操作是的,即该操作要么,要么。上述的代码无法得到应有的结果,就是因为 count++ 的操作不是一个具有原子性的操作。这三个操作可能会因为并发执行被随意地打断。

dalaoyuzuiniu的博客 2116

如何解决多线程的线程不安全问题

解决线程安全的思路和方法;1,使用没有共享资源的模型2.不对共享变量进行写操作3.通过加锁和一些原子操作来保证线程的顺序,可见性,和原子性。

huapiaoy的博客 1937

JAVA多线程安全问题解决办法

最后,为了提高性能,我们需要减少锁竞争、消除不必要的锁、进行锁粗化等优化。解决办法:使用原子类(如AtomicInteger、AtomicLong)或锁(如synchronized、ReentrantLock)保证操作的原子性。问题描述:在某些情况下,编译器可能会自动消除不必要的锁,但有时候程序员编写的不必要的同步代码会影响性能。解决办法:确保所有线程都能公平地获取到资源,可以使用公平锁(如ReentrantLock的公平模式)。解决办法:避免循环等待、避免资源不可抢占、避免持有多个锁、按顺序获取锁。

qa3629723的博客 901

多线程-线程安全问题解决方法

这里列举两种情况情况二👇一个字:贵 而且运行速度是相对的内存就已经很快了像这种bug情况由于随机排序 还有很多a)一个线程一把锁 -- 可重入b)两个线程两把锁b)M个线程N把锁。

C语言 1346

Java多线程(3)】线程安全问题解决方案

死锁的四个必要条件(重点) 1.锁具有互斥特性。(基本特点) 2. 锁不可抢占:一个线程拿到锁之后,除非它自己主动释放锁,否则别人抢不走。(基本特点) 3. 请求和保持:一个线程拿到一把锁之后,不释放这个锁的前提下,再尝试获取其他锁。(代码结构) 4. 循环等待:多个线程获取多个锁的过程中,出现了循环等待。如前面的场景2。(代码结构)

StuLearn-LJC的博客 1100

Java多线程并发安全问题

当多个线程并发操作同一临界资源,由于线程切换时机不确定,导致操作临界资源的顺序出现混乱严重时可能导致系统瘫痪.临界资源:操作该资源的全过程同时只能被单个线程完成.

是程序喵呀 1181

Java中的多线程安全问题解决方法

线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样 的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 我们通过一个案例,演示线程的安全问题: 电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “葫芦娃大战奥特曼”,本次电影的座位共100个 (本场电影只能卖100张票)。 我们来模拟电影院的售票窗口,实现多个窗口同时卖 “葫芦娃大战奥特曼”这场电影票(多个窗口一起卖这100张票) 需要窗口,采用线程对象来模拟;需要票,Runnab

Rocky balboa 2618

Java并发编程二】解决多线程安全问题的方法

我们使用多线程就好比一群滑稽老铁坐在同一张桌子前吃鸡,此时1号滑稽和2号滑稽同时看上了同一只鸡,1号滑稽碰到了鸡翅,2号滑稽碰到了鸡腿, 结果整只鸡被1号滑稽抢走了,此时二号滑稽就会非常生气(出现异常),可能会直接掀桌子(使整个进程被终止)。正常来说,按照锁的特性这个时候会出现死锁的情况,因为只有这个线程结束才能释放锁,而第二次加锁会使线程一直堵塞,无法结束,第二个锁无法得到锁对象永远无法解锁,陷入死循环。一个是用于线程之间的通信的,一个是让线程阻塞一段时间,唯一的相同点就是都可以让线程放弃执行一段时间。

2203_75565612的博客 951

java多线程会造成线程安全问题的原因总结

众所周知,多线程会造成线程安全问题,那么多线程为什么会导致线程安全问题呢? 一:首先了解jvm内存的运行时数据区 1.堆区:存储对象实例(和实例变量),数组等 2.java虚拟机栈(方法·栈),存放方法声明,局部变量,对象的引用变量,基本数据类型变量等 3.本地方法栈:存储一些本地方法(native关键字修饰的方法,如hashCode()方法,clone方法,Thread类的star0()方法) 4.方法区:存储类元数据,常量,静态变...

m0_57713282的博客 5169

java三种方法解决多线程安全问题

线程同步 当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。 要解决上述多线程并发访问一个资源的安全问题Java中提供了同步机制(synchronized)来解决。 为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制。 那么怎么去使用呢?有三种方式完成同步操作: 1.同步代码块。 2.同步方法。 3.锁机制。 (1)同步代码块 synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。 格式: synchron

qq_36044576的博客 1551

MTK平台WiFi学习--Wi-Fi 连线/断线问题初步分析

摘要:本文详细介绍了MTK平台WiFi连接问题的日志分析方法。文章首先阐述了WiFi连接的标准流程,包括指令接收、BSSID选择、信道分配、认证关联、四次握手和DHCP获取等关键步骤。针对常见故障场景,如选网失败、信道申请失败、认证关联失败、四次握手失败等,提供了具体的日志分析方法和排查思路,包括识别关键日志、分析状态码、结合固件日志和抓包数据等。文章还特别强调了不同故障场景下需要收集的日志类型和分析要点,为WiFi连接问题的定位提供了一套系统化的解决方案。

专业开发者的博客,一个从事Android WiFi和蓝牙的工程师 1553

第5章 Hadoop集群运行.pdf

第5章 Hadoop集群运行.pdf

上一篇: 算法设计学习笔记
下一篇: 直播推流神器 Kplayer 手把手教你在B站7*24h全天直播
IShowCode
博客等级 码龄7年 622粉丝 · 20原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IShowCode

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值