Java解决线程安全问题

2023-11-16

背景

原文地址:https://duktig.cn/archives/36/

1. 线程安全问题

1.1 什么是线程安全?

线程安全:多个线程不管以何种方式访问某个类,并且在主调代码中不需要进行同步,都能表现正确的行为。

“线程安全”不是指线程的安全,而是指内存的安全。为什么如此说呢?这和操作系统有关。

目前主流操作系统都是多任务的,即多个进程同时运行。为了保证安全,每个进程只能访问分配给自己的内存空间,而不能访问别的进程的,这是由操作系统保障的。但是进程中的多个线程共享进程的堆内存,这就是造成问题的潜在原因。

假设某个线程把数据处理到一半,觉得很累,就去休息了一会,回来准备接着处理,却发现数据已经被修改了,不是自己离开时的样子了。可能被其它线程修改了。

比如把你住的小区看作一个进程,小区里的道路/绿化等就属于公共区域。你拿1万块钱往地上一扔,就回家睡觉去了。睡醒后你打算去把它捡回来,发现钱已经不见了。可能被别人拿走了。因为公共区域人来人往,你放的东西在没有看管措施时,一定是不安全的。内存中的情况亦然如此。

所以线程安全指的是,在堆内存中的数据由于可以被任何线程访问到,在没有限制的情况下存在被意外修改的风险。

即堆内存空间在没有保护机制的情况下,对多线程来说是不安全的地方,因为你放进去的数据,可能被别的线程“破坏”。

1.2 产生的原因

  1. 多个线程执行的不确定性引起执行结果的不稳定。
  2. 多个线程对数据的共享,会造成操作的不完整性,会破坏数据。

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

1.3 实例(买票超卖问题)

/**
 * 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式
 *
 * 存在线程的安全问题,待解决。
 */
class Window extends Thread{

    private static int ticket = 100;
    @Override
    public void run() {

        while(true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(ticket > 0){
                System.out.println(getName() + ":卖票,票号为:" + ticket);
                ticket--;
            }else{
                break;
            }
        }

    }
}

public class WindowTest {
    public static void main(String[] args) {
        Window t1 = new Window();
        Window t2 = new Window();
        Window t3 = new Window();

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

    }
}

结果

出现了共享数据错误

窗口2:卖票,票号为:100
窗口1:卖票,票号为:100
窗口3:卖票,票号为:98
窗口3:卖票,票号为:97
窗口2:卖票,票号为:97
窗口1:卖票,票号为:95
窗口1:卖票,票号为:94
窗口2:卖票,票号为:93
窗口3:卖票,票号为:92
窗口3:卖票,票号为:91
......
窗口1:卖票,票号为:10
窗口3:卖票,票号为:10
窗口2:卖票,票号为:10
窗口2:卖票,票号为:7
窗口3:卖票,票号为:7
窗口1:卖票,票号为:5
窗口3:卖票,票号为:4
窗口2:卖票,票号为:4
窗口1:卖票,票号为:2
窗口2:卖票,票号为:1
窗口3:卖票,票号为:1

分析

  1. 问题:三条线程同时共享ticket的票,卖票过程中,出现了重票、错票 -->出现了线程的安全问题,导致数据错误。
  2. 原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
  3. 解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。

1.4 如何确定是否存在线程安全问题?

  • 明确哪些代码是多线程运行的代码
  • 明确多个线程是否有共享数据
  • 明确多线程运行代码中是否有多条语句操作共享数据

2. 如何解决线程安全问题?

如何解决线程安全问题?解决的过程其实就是一个取舍的过程,不同的方案有不同的侧重点。

2.1 不可变(Immutable)

不可变的对象一定是线程安全的,不需要采取任何的线程安全措施。只要一个不可变的对象被正确的构建出来,在多线程的状态下也不允许修改,那么一定不会发生不一致的状态。

形象比喻:只能看,不能摸。自然不会存在线程安全问题。

在多线程的环境下,应尽量是对象成为不可变的状态,来满足线程安全,这基本也是最小的开销方式来保证线程安全。

不可变类的意思是创建该类的实例后,该实例的实例变量是不可改变的。

不可变的类型:

  • String
  • 枚举类型
  • 基本数据类型的包装类以及BigIntegerBigDecimal 等大数据类型
  • final关键字修饰的基本数据类型

注:final修饰的引用数据类型不可变,但其成员变量的类可能会发生改变

2.2 变量私有化

要保证线程安全,并不是一定就要进行同步。如果一个方法本来就不涉及共享数据,那它自然就无须任何同步措施去保证正确性。

2.2.1 栈封闭(主要为局部变量)

多个线程访问同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在虚拟机栈中,属于线程私有的

形象比喻:私有的东西就不该让别人知道

如果一些数据只有某个线程会使用,其它线程不能操作也不需要操作,这些数据就可以放入线程的栈内存中。较为常见的就是局部变量。

double avgScore(double[] scores) {
    double sum = 0;
    for (double score : scores) {
        sum += score;
    }
    int count = scores.length;
    double avg = sum / count;
    return avg;
}

这里的变量sumcountavg都是局部变量,它们都会被分配在线程栈内存中。

假如现在A线程来执行这个方法,这些变量会在A的栈内存分配。与此同时,B线程也来执行这个方法,这些变量也会在B的栈内存中分配。

也就是说这些局部变量会在每个线程的栈内存中都分配一份。由于线程的栈内存只能自己访问,所以栈内存中的变量只属于自己,其它线程根本就不知道,也就不存在线程安全问题。

问题

不可能所有的变量都只声明成局部变量,而只供某个线程使用,去解决线程安全问题。如果想要声明成员变量,还想要保证线程安全怎么办?

可以使用ThreadLocal,使每个线程都私有化一份这个变量的本地副本,互相不受影响。

2.2.2 线程本地存储(Thread Local Storage)

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

可以使用java.lang.ThreadLocal类来实现线程本地存储功能。

通俗来说:要让公共区域堆内存中的数据对于每个线程都是安全的,那就每个线程都拷贝它一份,每个线程只处理自己的这一份拷贝而不去影响别的线程的,这不就安全了嘛。

形象比喻:大家不要抢,人人有份

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

为了理解 ThreadLocal,先看以下代码:

public class ThreadLocalExample1 {
    public static void main(String[] args) {
        ThreadLocal threadLocal1 = new ThreadLocal();
        ThreadLocal threadLocal2 = new ThreadLocal();
        Thread thread1 = new Thread(() -> {
            threadLocal1.set(1);
            threadLocal2.set(1);
        });
        Thread thread2 = new Thread(() -> {
            threadLocal1.set(2);
            threadLocal2.set(2);
        });
        thread1.start();
        thread2.start();
    }
}

它所对应的底层结构图为:

ThreadLocal实例内存结构

ThreadLocal实例内存结构

每个 Thread 都有一个 ThreadLocal.ThreadLocalMap 对象。

/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

当调用一个 ThreadLocalset(T value) 方法时,先得到当前线程的 ThreadLocalMap 对象,然后将 ThreadLocal->value 键值对插入到该 Map 中。

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

get() 方法类似。

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

ThreadLocal 从理论上讲并不是用来解决多线程并发问题的,因为根本不存在多线程竞争。ThreadLocal就是,把一个数据复制N份,每个线程认领一份,各玩各的,互不影响。

在一些场景 (尤其是使用线程池) 下,由于 ThreadLocal.ThreadLocalMap 的底层数据结构导致 ThreadLocal 有内存泄漏的情况,应该尽可能在每次使用 ThreadLocal 后手动调用 remove(),以避免出现 ThreadLocal 经典的内存泄漏甚至是造成自身业务混乱的风险

2.3 互斥同步

前面给出的一些方案,有点“理想化”了,现实中的情况其实是非常混乱嘈杂的,没有规则的。

形象比喻:没有规则,那就先入为主

例子:

比如在中午高峰期你去饭店吃饭,进门后发现只剩一个空桌子了,你心想先去点餐吧,回来就坐这里吧。当你点完餐回来后,发现已经被别人捷足先登了。

因为桌子是属于公共区域的物品,任何人都可以坐,那就只能谁先抢到谁坐。虽然你在人群中曾多看了它一眼,但它并不会记住你容颜。

解决方法就不用我说了吧,让一个人在那儿看着座位,其它人去点餐。这样当别人再来的时候,你就可以理直气壮的说,“不好意思,这个座位,我,已经占了”。

相信聪明的你已经猜到了我要说的东西了,没错,就是互斥锁

如果公共区域(堆内存)的数据,要被多个线程操作时,为了确保数据的安全(或一致)性,需要在数据旁边放一把锁,要想操作数据,先获取锁再说吧。

假设一个线程来到数据跟前一看,发现锁是空闲的,没有人持有。于是它就拿到了这把锁,然后开始操作数据。

这时,又来了一个线程,发现锁被别人持有着,按照规定,它不能操作数据,因为它无法得到这把锁。当然,它可以选择等待,或放弃,转而去干别的。

因为第一个线程持有锁,可以大胆干事而不用担心其他线程的影响。

对于互斥同步锁,可以使用synchronizedReentrantLock

class ClassAssistant {

    double totalScore = 60;
    final Lock lock = new Lock();

    void addScore(double score) {
        lock.obtain();
        totalScore += score;
        lock.release();
    }

    void subScore(double score) {
        lock.obtain();
        totalScore -= score;
        lock.release();
    }
}

假定一个班级的初始分数是60分,这个班级抽出10名学生来同时参加10个不同的答题节目,每个学生答对一次为班级加上5分,答错一次减去5分。因为10个学生一起进行,所以这一定是一个并发情形。

因此加分和减分这两个方法被并发的调用,它们共同操作总分数。为了保证数据的一致性,需要在每次操作前先获取锁,操作完成后再释放锁。

问题

互斥阻塞会有线程阻塞和唤醒所带来的性能问题。

2.4 非阻塞同步

互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出现问题。无论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。

随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略:先进行操作,如果没有其它线程争用共享数据,那操作就成功了,否则采取补偿措施(不断地重试,直到成功为止)。这种乐观的并发策略的许多实现都不需要将线程阻塞,因此这种同步操作称为非阻塞同步。

形象比喻:相信世界充满爱,即使被伤害

由于无锁操作中没有锁的存在,因此不可能出现死锁的情况,也就是说乐观锁天生免疫死锁

乐观锁多用于“读多写少“的环境,避免频繁加锁影响性能;而悲观锁多用于”写多读少“的环境,避免频繁失败和重试影响性能。

2.4.1 CAS

例子解释:

例子,假如你往地上仍1万块钱,是不是一定会丢呢?这要看情况了,如果是在人来人往的都市,可以说肯定会丢的。如果你跑到无人区扔地上,可以说肯定不会丢。

可以看到,都是把东西无保护的放到公共区域里,结果却相差很大。这说明安全问题还和公共区域的环境状况有关系。

比如我把数据放到公共区域的堆内存中,但是始终都只会有1个线程,也就是单线程模型,那这数据肯定是安全的。

再者说,2个线程操作同一个数据和200个线程操作同一个数据,这个数据的安全概率是完全不一样的。肯定线程越多数据不安全的概率越大,线程越少数据不安全的概率越小。取个极限情况,那就是只有1个线程,那不安全概率就是0,也就是安全的。

因为锁的获取和释放是要花费一定代价的,如果在线程数目特别少的时候,可能可能就不会有别的线程来操作数据,此时你还要获取锁和释放锁,可以说是一种浪费。

针对这种“地广人稀”的情况,专门提出了一种方法,叫CAS。就是在并发很小的情况下,数据被意外修改的概率很低,但是又存在这种可能性,此时就用CAS。

CAS的全称是:比较并交换(Compare And Swap)。在CAS中,有这样三个值:

  • V:要更新的变量(var)——内存地址
  • E:预期值(expected)——旧值
  • N:新值(new)

比较并交换的过程如下:

判断V是否等于E,如果等于,将V的值设置为N;如果不等,说明已经有其它线程更新了V,则当前线程放弃更新,什么都不做。

我们以一个简单的例子来解释这个过程:

  1. 如果有一个多个线程共享的变量i原本等于5,我现在在线程A中,想把它设置为新的值6;
  2. 我们使用CAS来做这个事情;
  3. 首先我们用i去与5对比,发现它等于5,说明没有被其它线程改过,那我就把它设置为新的值6,此次CAS成功,i的值被设置成了6;
  4. 如果不等于5,说明i被其它线程改过了(比如现在i的值为2),那么我就什么也不做,此次CAS失败,i的值仍然为2。

在这个例子中,i就是V,5就是E,6就是N。

那有没有可能我在判断了i为5之后,正准备更新它的新值的时候,被其它线程更改了i的值呢?

不会的。因为CAS是一种原子操作,它是一种系统原语,是一条CPU的原子指令,从CPU层面保证它的原子性。

CAS是一种原子操作,在Java中,有一个Unsafe类,它在sun.misc包中。它里面是一些native方法,其中就有几个关于CAS的,他们都是public native的。

当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。

ABA问题?(狸猫换太子)

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

ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A→B→A就会变成1A→2B→3A。

Java 1.5开始,JDK的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

2.4.2 Atomic(原子操作)

Unsafe类支持CAS的方法。那Java具体是如何使用这几个方法来实现原子操作的呢?

JDK提供了一些用于原子操作的类,在java.util.concurrent.atomic包下面。在JDK 8中,有如下17个类:

java.util.concurrent.atomic

从名字就可以看得出来这些类大概的用途:

  • 原子更新基本类型
  • 原子更新数组
  • 原子更新引用
  • 原子更新字段(属性)

3. 总结和分析

“栈封闭”:找个只有自己知道的地方藏起来,当然安全了。

ThreadLocal:每人复制1份,各玩各的,互不影响,当然也安全了。

“不可变”:更狠了,直接规定,只能读取,禁止修改,当然也安全了。

互斥同步非阻塞同步,分别对应悲观锁乐观锁的策略。

参考

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java解决线程安全问题 的相关文章

  • 枚举的子类化

    有没有一种简单的方法来子类化Javaenum 我问这个问题是因为我有大约 10 个实现相同接口的对象 但它们对某些方法也有相同的实现 因此我想通过将所有相同的实现放置在扩展的中间对象中来重用代码Enum它也是我需要的所有其他类的超类 或许事
  • “源兼容性”和“目标兼容性”有什么区别?

    之间有什么关系 区别sourceCompatibility and targetCompatibility 当它们设置为不同的值时会发生什么 根据工具链和兼容性 https docs gradle org current userguide
  • 我们可以有条件地声明 spring bean 吗?

    有没有一种方法可以有条件地声明 Spring bean 例如
  • JOOQ 忽略具有默认值的数据库列

    看来JOOQ完全忽略了数据库列的默认值 既不会更新 ActiveRecord 对象 也不会在 INSERT 时跳过此列 相反 它尝试将其设置为 NULL 这在 NOT NULL 列上失败 Example CREATE TABLE bug f
  • 如何使用 Java 引用释放 Java Unsafe 内存?

    Java Unsafe 类允许您按如下方式为对象分配内存 但是使用此方法在完成后如何释放分配的内存 因为它不提供内存地址 Field f Unsafe class getDeclaredField theUnsafe Internal re
  • getCurrentSession 在网络中休眠

    我正在使用 hibernate 和 jsp servlet 编写一个基于 Web 的应用程序 我读过有关sessionFactory getCurrentSession and sessionFactory openSession方法 我知
  • 2^31 次方的 Java 指数错误 [重复]

    这个问题在这里已经有答案了 我正在编写一个java程序来输出2的指数幂 顺便说一句 我不能使用Math pow 但是在 2 31 和 2 32 处我得到了其他东西 另外 我不打算接受负整数 My code class PrintPowers
  • 如何使用双重调度来分析图形基元的交集?

    我正在分析图形基元 矩形 直线 圆形等 的交互并计算重叠 相对方向 合并等 这被引用为双重调度的一个主要示例 例如维基百科 http en wikipedia org wiki Double dispatch 自适应碰撞算法通常要求 不同的
  • 如何自定义舍入形式

    我的问题可能看起来很简单 但仍然无法得到有效的东西 我需要自定义 Math round 舍入格式或其他格式以使其工作如下 如果数字是 1 6 他应该四舍五入到 1 如果大于或等于 1 7 他应该四舍五入到 2 0 对于所有其他带有 6 的小
  • Android - 存储对ApplicationContext的引用

    我有一个静态 Preferences 类 其中包含一些应用程序首选项和类似的内容 可以在那里存储对 ApplicationContext 的引用吗 我需要该引用 以便我可以在不继承 Activity 的类中获取缓存文件夹和类似内容 你使用的
  • 按降序排序映射java8 [重复]

    这个问题在这里已经有答案了 private static
  • Tomcat 6 未从 WEB-INF/lib 加载 jar

    我正在尝试找出我的 tomcat 环境中的配置问题 我们的生产服务器正在运行 tomcat 安装并从共享 NFS 挂载读取战争 然而 当我尝试使用独立的盒子 及其配置 进行同样的战争时 我收到下面发布的错误 有趣的是 如果我将 WEB IN
  • 文本视图不显示全文

    我正在使用 TableLayout 和 TableRow 创建一个简单的布局 其中包含两个 TextView 这是代码的一部分
  • Lombok 不适用于 Eclipse Neon

    我下载了lombok jar lombok 1 16 14 jar 并将其放入我的下载中 然后我点击这个 jar 执行正确地识别了我的 MacOS 上的 Eclipse 实例 然后我选择了我想要的实例 Lombok也在pom xml中指定
  • 我们如何使用 thymeleaf 绑定对象列表的列表

    我有一个表单 用户可以在其中添加任意数量的内容表对象这也可以包含他想要的列对象 就像在 SQL 中构建表一样 我尝试了下面的代码 但没有任何效果 并且当我尝试绑定两个列表时 表单不再出现 控制器 ModelAttribute page pu
  • 即使禁用安全性,OAuth 令牌 API 也无法在 Elastic Search 中工作

    我是 Elastic search 新手 使用 Elastic search 版本 7 7 1 我想通过以下方式生成 OAuth 令牌弹性搜索文档 https www elastic co guide en elasticsearch re
  • 我所有的 java 应用程序现在都会抛出 java.awt.headlessException

    所以几天前我有几个工作Java应用程序使用Swing图书馆 JFrame尤其 他们都工作得很好 现在他们都抛出了这个异常 java awt headlessexception 我不知道是什么改变了也许我的Java版本不小心更新了 谢谢你尽你
  • 检测到 JVM 正在关闭

    我有一个使用 addShutdownHook 处理 Ctrl C 的 Swing 应用程序 它工作正常 直到我的关闭任务之一调用一个在正常情况下更改 JLabel 文本的函数 此时它挂起 我认为问题是 Swing EDT 已终止或正在等待某
  • 使用 DBCP 配置 Tomcat

    在闲置一段时间 几个小时 后 我们收到了 CommunicationsException 来自 DBCP 错误消息 在异常中 位于这个问题的末尾 但我没有看到任何配置文件中定义的 wait timeout 我们应该看哪里 在 tomcat
  • Spring 作为 JNDI 提供者?

    我想使用 Spring 作为 JNDI 提供程序 这意味着我想在 Spring 上下文中配置一个 bean 可以通过 JNDI 访问该 bean 这看起来像这样

随机推荐

  • 关闭cmd或其它win exe程序方法python

    import os def kill exe exe name os system taskkill f t im exe name MESMTPC exe程序名字 print 关闭进程 0 format exe name 例如 exe n
  • 一文搞懂MySQL索引(实现原理加优化实战,面试必问)

    前言 本篇文章从数据结构 B Tree的构建过程 MySQL索引实现 索引为什么那么快 MySQL有哪些索引 聚集索引和二级索引的区别 索引失效的原因 EXPLAIN关键字分析 索引实战 索引的优缺点 什么时候应该加索引 全方面帮助读者理解
  • Error in: PCL_DEPRECATED_HEADER(1, 15, “Please include pcl/common/io.h directly.“)

    error error expected constructor destructor or type conversion before token PCL DEPRECATED HEADER 1 15 Please include pc
  • 【Pip和Conda安装包的区别】

    Pip和Conda都是用于Python软件包管理的工具 但它们有以下区别 包管理器 Pip是Python的默认包管理器 而Conda是Anaconda发行版的包管理器 跨平台支持 Pip在各个平台上都可以使用 但是Conda特别适用于跨平台
  • VUE 出现登录界面但控制台报错FAILED TO LOAD RESOURCE: NET::ERR_CONNECTION_REFUSED

    首先 vue项目运行的端口号一般为8080 地址出现8080 1 2 说明8080端口被占用 导致vue项目在别的端口运行 但会与本地后台端口对应不上报如标题所示错误 解决方法 1 查看端口被哪个进程占用 输入命令 netstat ano
  • crm项目的搭建

    一 创建Maven项目 1 选择Maven下的 org jetbrains idea maven model Maven Archetype webapp 2 三板斧 坐标 GroupId com shsxt ArtifactId shsx
  • Fabric实战(13)Fabric链码调试(容器外)

    链码调试 本文章所有操作基于的操作系统版本是 ubuntu16 04 64位 本文章基于的Fabric网络环境是 Fabric实战 2 运行一个简单的fabric网络 容器外 1 开发环境链码调试 1 1 容器之外运行Chaincode 第
  • 开机出现start pxe over ipv4 /start pxe over ipv6无法进入系统?!

    我遇到的是戴尔电脑start pxe over ipv4 出现此类问题的原因 用户将win10系统装成win7后出现的 一般是由于在重装系统之前在BIOS中不小心设置错误所引起的 解决方法 方法一 1 首先进入bios 不同品牌按不同的热键
  • C# 通过 RabbitMQ 实现定时任务 (延时队列)

    环境准备 需要在MQ中进行安装插件 地址链接插件介绍地址 https www rabbitmq com blog 2015 04 16 scheduling messages with rabbitmq 使用场景 作为一个新的预支付订单被初
  • 部署SpringBoot项目到云服务器

    服务器选择以及项目背景 我购买的是阿里云ECS服务器 它的特点是可以给我们配置服务器较大的自由度 我选择的是Centos Linux操作系统 我这次是希望在服务器上部署一个SpringBoot后台项目 最后实现的效果是我可以在手机App上通
  • Vivado的一些tcl命令记录(待补充)

    1 Report Clock Networks report clock networks name network 1 2 分析设计中逻辑级数的分布 report design analysis logic level distribut
  • NLP(自然语言处理)是什么?

    NLP基本概念 自然语言处理 Natural Language Processing NLP 是以语言为对象 利用计算机技术来分析 理解和处理自然语言的一门学科 即把计算机作为语言研究的强大工具 在计算机的支持下对语言信息进行定量化的研究
  • simplest-jpa v1.2.0如何优雅实现多租户

    开始使用 simplest详细文档 simplest jpa 使用多租户需要 2 个步骤 在属性中配置对应租户表和列 配置 TenantFactory 注入租户数据源 TenantFactory 是用于生产租户 ID 的 或者说是用于获取当
  • idea 内存不足 low memory 彻底解决

    1 在IDE中 帮助 help gt 编辑自定义vm配置 idea64 exe vmoptions文件 修改 Xmx2048m Xms2048m 增加根据自己的系统内存 此时重启idea 仍然报内存不足 提示提高内存 通过idea log发
  • Loader Runner 课程笔记(一)录制设置和压测

    1 录制前设置 1 创建脚本 新建单协议脚本 选择Web协议 创建 LR11只支持WIN7系统 浏览器IE8 9和低版本的火狐 24 0或36 0 高版本IE可以卸载装IE8或9 不支持谷歌 LR自带火狐路径HP LoadRunner bi
  • 关于ECC-Elgamal同态加密

    关于ECC Elgamal同态加密 1 什么是ECC elliptic curve 1 有限域 首先我们要知道椭圆曲线加密是在有限域进行加密的 对于无限域上的加密我没有了解过 在椭圆曲线 加密上有限域分为 1 GF p 素数域2 GF 2
  • Python 爬虫案例

    一 用cookie池模拟登录 在网络请求交互中 为了维持用户的登录状态 引入了cookie的概念 当用户第一次登录某个网站时 网站服务器会返回维持登录状态需要用到的信息 这些信息就称为cookie 浏览器会将cookie信息保存在本地计算机
  • Intellij IDEA快速实现Docker镜像部署

    1 Docker开启远程访问 root izwz9eftauv7x69f5jvi96z docker vim lib systemd system docker service 修改ExecStart这行 ExecStart usr bin
  • python中各种文件类型的读写

    本文汇总了在python中各种类型文件的读取和写入 包含文本 图像 表格 log文件 pickle文件 npy文件 npz文件等 文本类型 txt文件 json文件 yaml文件 图像类型 使用skimage PIL opencv imag
  • Java解决线程安全问题

    文章目录 背景 1 线程安全问题 1 1 什么是线程安全 1 2 产生的原因 1 3 实例 买票超卖问题 1 4 如何确定是否存在线程安全问题 2 如何解决线程安全问题 2 1 不可变 Immutable 2 2 变量私有化 2 2 1 栈