【java篇】线程安全问题(大总结)

2023-11-19

哎嘿,CSDN的大佬您来啦,这来都来了,浅浅的给个赞呗!!!

 系列文章目录

线程的创建与主要方法分析和其他基础知识点;可以参考以下文章

线程知识点总结_南斋孤鹤的博客-CSDN博客_线程知识(超全)线程知识点、及线程方面的一些理解性问题https://blog.csdn.net/m0_64231944/article/details/124069105?spm=1001.2014.3001.5502


目录

系列文章目录

前言

一、什么是线程安全问题?

二、为什么会出现线程安全问题呢?

三、造成线程安全问题的原因还有那些?

2.问题解答:

四、那么出了线程安全问题我们如何解决线程安全问题呢?

方式一:同步代码块synchronized( 同步监视器){//需要被同步的代码

方式二:同步方法如果操作共享数据的代码我们封装在一个方法内,我们不妨将此方法声明为同步的(使用synchronized修饰方法);

方式三:lock锁(JDK5.0新增);

方式四:使用原子变量(有弊端,谨慎使用)

五、买票例子代码实现:

总结



前言

线程安全是在项目中使用线程的关键点,尤其是在多线程的使用中,线程安全是很重要的,下面我们来详细介绍线程安全问题的出现原因解决办法


一、什么是线程安全问题?

线程安全是多线程领域的问题,(线程安全问题就是在多线程环境中 , 并且存在数据共享 (即多个线程操作同一个数据))线程安全可以简单理解为一个方法或者一个实例可以在多线程环境中使用而不会出现问题。 在同一程序中运行多个线程本身不会导致问题,问题在于多个线程访问了相同的资源。

在程序中如果使用成员变量, 且对成员变量进行数据修改 , 就存在数据共享问题, 也就会出现线程安全问题。

二、为什么会出现线程安全问题呢?

在 Java 程序中,存储数据的内存空间分为共享内存和本地内存。线程在读写主存的共享变量时,会先将该变量拷贝一份副本到自己的本地内存,然后在自己的本地内存中对该变量进行操作,完成操作之后再将结果同步至主内存。主内存数据和本地内存的不同步,导致多个线程同时操作主内存里的同一个变量时,变量数据可能会遭到破坏。可以举个例子来解释:

在存在多线程的环境中,并且有共享的数据,这中情况下就可能会出现线程安全问题:即一个线程访问的共享数据被其他线程修改了, 那么此时就发生了线程安全问题 。我们举一个例子:现有以int value=0;有A、B两个线程操作value,A线程进行+1操作,B线程进行+3操作同一时刻,AB、同时访问value的值,获取的结果都是0;然后同时进行操作,这个时候就会出现bug,value的值就可能是1或3 了,这就是出现了线程安全问题。
 

三、造成线程安全问题的原因还有那些?

     下面我们提出的以下问题:后面我们会逐一解答;

1)抢占式执行,调度过程随机(什么是抢占式呢?);

2)多个线程同时修改同一个变量(可以适当调整代码结构,避免这种情况);

3)针对变量的操作,不是原子的(什么是原子变量?是不是将变量定义为原子性后就一定能解决问题?);

4)内存可见性,一个线程频繁读,一个线程写(什么是内存可见性?);

2.问题解答:

2.1、什么是抢占式呢?

        这是一种CPU的调度方式:程序根据某种原则,去暂停某个正在执行的进程,将已分配给该线程的CPU的执行权重新分配给另一个线程,举例:也就是A线程在拥有CPU执行权的情况下,B线程抢断了A线程的执行权,进行B线程的部分操作。

为加深理解,我们拓展下与之对应的CPU执行方式:非抢占式方式:在采用这种调度方式时,一旦把CPU的执行权分配给某个线程后,就一直让它运行下去,决不会因为任何其它原因去抢占当前正在运行的线程的CPU执行权,直至该线程完成他的所有操作,或发生某事件而被阻塞时,才把执行权分配给其它线程。

2.1、什么是原子变量?

        说起原子变量,首先我们要理解原子是什么东西,说到原子,我们是熟悉的,我们会想到我们在化学中学习到的化学式,比如水分子的化学式,它有以下特性:对原子变量的操作是一个整体,是不会分割的(例如:compareAndSet,跨越到原子变量中,原子变量就是在变量的基础上添加了原子性,那么原子变量的原子性体现在哪里呢?原子变量我们可以理解为一个特殊的变量类型(实际是一个特殊类),java对他有专门的处理原子变量的独有的方法,比如 提供的特有方法boolean compareAndSet(expectedValue, updateValue) 的功能,compare和set这两个操作是不可分割的,这就是原子性的操作。

总结:

原子性是指一个操作,要么全部执行并且执行过程不会被打断,要么就都不执行。

  • Java 语言本身只保证了基本类型变量的读取和赋值是原子性操作。
  • 简单操作的原子性可以通过原子类实现。
  • 通过 synchronized 和 ReenTrantLock 等锁结构可以保证更大范围的原子性。

原子变量的特殊之处(特有方法的不可分割性),就可以避免出现上文提到的多个线程操作同一个共享数据引起的线程安全问题。

提到原子变量不得不提:原子变量相关的CAS算法
  CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并发访问(这是书上的难以理解的屁话,我们不用管,只需要知道他是操作原子变量的一个方法)。
  CAS 是一种无锁的非阻塞算法的实现。
  CAS 中包含了3 个操作数:
    1、需要读写的内存值V
    2、进行比较的值A
    3、拟写入的新值B
    4、当且仅当V 的值等于A 时,CAS 通过原子方式用新值B 来更新V 的值,否则不会执行任何操作

下面以原子变量类型中的AtomicInteger(其他还有Atomiclong等等原子类型)简略示例使用步骤:
 

class MyRunnable2 implements Runnable{
    private AtomicInteger serialNumber = new AtomicInteger(0);
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":"+getAndAdd());
    }

    private int getAndAdd(){
        return serialNumber.getAndIncrement();
    }
}

 其他常用方法:

以下为方法为AtomicInteger基于原子操作常用方法

//获取当前原子变量中的值并为其设置新值

public final int getAndSet(int newValue)

//比较当前的value是否等于expect,如果是设置为update并返回true,否则返回false

public final boolean compareAndSet(int expect, int update)

//获取当前的value值并自增一

public final int getAndIncrement()

//获取当前的value值并自减一

public final int getAndDecrement()

//获取当前的value值并为value加上delta

public final int getAndAdd(int delta)

2.3、什么是内存可见性?

内存可见性就是(我们引用之前提到的A、B线程操作value值的例子),A线程操作value的值的结果B会知道(A的操作对B可见),B修改value的值的结果A线程也会道(B的操作对A可见),这样value的结果就只有一种情况了value=4;

总结:所谓内存可见性就是A线程对共享内存的操作其他线程可见;

总结:

可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

  • Java 语言会尽可能保证主内存数据和本地内存同步,但仍可能出现不可见问题。
  • 通常用 volatile 关键字来保证可见性。
  • 通过 synchronized 和 ReenTrantLock 等锁结构在释放锁之前会将对变量的修改刷新到主存当中,也能够保证可见性。

volatile 关键字

修饰成员变量,每次被线程访问时,强迫从主存中读写该成员变量的值。

volatile 关键字只能保证可见性,不能保证原子性。多个线程同时操作主内存里的同一个变量时,变量数据仍有可能会遭到破坏。

  • 线程执行过程中如果 CPU 一直满载运转,就会默认使用本地内存中的值,而没有空闲读取主存同步数据。
  • 线程执行过程中一旦 CPU 获得空闲,JVM 也会自动同步主存数据,尽可能保证可见性。
public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        MyThread t = new MyThread();
        t.start();
        try { 
            Thread.sleep(1000); 
        } catch(InterruptedException e) {}
        t.setRun(false);      
    }
}

class MyThread extends Thread {
    // 添加 volatile 关键字,强制同步主存数据。
    // 删除 volatile 关键字,子线程将始终读取本地内存中 true 副本:陷入死循环。
    private volatile boolean run = true;             
    public void setRun(boolean run) { this.run = run; }
    @Override
    public void run() {
        while (this.run == true) {
            int a = 2;
            int b = 3;
            int c = a + b;
            // System.out.print("CPU rest");      打印输出时 CPU 获得空闲,自动同步主存数据。
        }
        System.out.print("end");   
        return;
    }
}

synchronized关键字

修饰方法或代码块。被线程访问时由线程抢占锁,直到执行完毕后自动释放锁。其他线程没有获得锁将无法访问上锁内容。保证了指定内容在同一时刻只有一个线程能访问。

  1. 修饰 static 方法实质是给当前类上锁:这个类的所有同步 static 方法共享一个锁。
  2. 修饰实例方法实质是给对象上锁:这个对象内所有的 synchronized 实例方法共享一个锁。

每一个对象都有且仅有一个与之对应的 monitor 对象。synchronized 关键字修饰方法时会对方法添加标志位,当线程执行到某个方法时,JVM会去检查该方法的访问标志是否被设置,如果设置了线程会先获取这个对象所对应的 monitor 对象,再执行方法体,方法执行完后释放 monitor 。

同步代码块则是在同步代码块前插入 monitorenter ,在同步代码块结束后插入 monitorexit 。

public class ThreadDemo {
    public static void main(String[] args) {
        ThreadDemo test = new ThreadDemo();
        new Thread(test::m1).start();
        new Thread(test::m2).start();   
    }

    public synchronized void m1() {
        System.out.println("1");
        try { 
            Thread.sleep(1000); 
        } catch(InterruptedException e) {}
        System.out.println("2");
    }

    public synchronized void m2() {
        System.out.println("3");
        try { 
            Thread.sleep(500); 
        } catch(InterruptedException e) {}
        System.out.println("4");
    }
}

 二者的异同:

     1. volatile 关键字用于修饰变量,synchronized 关键字用于修饰方法以及代码块。

  1. volatile 关键字是数据同步的轻量级实现,性能比 synchronized 关键字更好。

  2. volatile 关键字被多线程访问不会发生阻塞,synchronized 关键字可能发生阻塞。

  3. volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。

四、那么出了线程安全问题我们如何解决线程安全问题呢?

我们还是通过一个例子来讲解理顺解决方法和思路;

例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式

其他创建线程的方式可以参考

线程知识点总结_南斋孤鹤的博客-CSDN博客_线程知识(超全)线程知识点、及线程方面的一些理解性问题https://blog.csdn.net/m0_64231944/article/details/124069105?spm=1001.2014.3001.5502

此时的线程问题为:卖票过程中,出现了重票、错票-->出现了线程的安全问题
②.问题出现的原因:当某个线程操作共享数据(车票)的过程中,尚未操作完成时,其他线程参与进来,也同时操做共享数据(车票)。

③.如何解决:当一个线程a在操作共享数据(车票ticket)的时候,其他线程不能参与进来。直到线程a操作完共享数据(车票ticket)之后,所有线程在重新争抢CPU的执行权,谁抢到谁再操作共享数据。这种情况即使线程出现了阻塞,也不会出现多个线程同时出售同一张票。

④.在Java中,我们通过同步机制,来解决线程的安全问题。

方式一:同步代码块
synchronized( 同步监视器){
//需要被同步的代码}

说明:

1.操作共享数据的代码,即为需要被同步的代码;要包的不多不少

2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。

3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁,我们常用object对象。

要求:多个线程必须要共用同一把锁。

补充:根据实际条件我们可以用this充当锁(继承Thread实现多线程时)我们也可以用类充当监视器,类也是对象。

方式二:同步方法
如果操作共享数据的代码我们封装在一个方法内,我们不妨将此方法声明为同步的(使用synchronized修饰方法);

也有锁,锁是this;

方式三:lock锁
(JDK5.0新增);

实现线程(实现Runnable)。
实例化Reentrantlock;
手动调用锁定方法lock();
手动调用解锁方法unlock();
下面我们演示一个案例,调用三个线程来出售100张电影票

方式四:使用原子变量(有弊端,谨慎使用)

        有什么弊端呢,原子变量使用在存在多线程和有共享变量的程序中,他的弊端就体现在操作共享变量的线程不能太多,太多之后就会出现卡顿,性能下降,倒还不如使用synchronized关键字了。

所以:谨慎使用原子变量。

五、买票例子代码实现:

public class MyRunnableDemo {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
 
        Thread t1 = new Thread(mr,"第一窗口");
        Thread t2 = new Thread(mr,"第二窗口");
        Thread t3 = new Thread(mr,"第三窗口");
 
        t1.start();
        t2.start();
        t3.start();
    }
}
public class MyRunnable implements Runnable {
    private int tickets = 100;
    private Object obj = new Object();
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + "开始售第:" + tickets + "张票");
                    tickets--;
                } else {
                    break;
                }
            } finally {
                lock.unlock();
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

代码效果你可以复制代码到idea中查看 


总结

以上就是今天要记录的内容,本文仅仅简单介绍了线程安全问题的一些相关知识,这仅仅是我对于该问题的认识和理解,不完全正确、并且后续会不断改进,希望可以帮到各位CSDN的读者。

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

【java篇】线程安全问题(大总结) 的相关文章

  • Grails 3.x bootRun 失败

    我正在尝试在 grails 3 1 11 中运行一个项目 但出现错误 失败 构建失败并出现异常 什么地方出了错 任务 bootRun 执行失败 进程 命令 C Program Files Java jdk1 8 0 111 bin java
  • 在 Java 中连接和使用 Cassandra

    我已经阅读了一些关于 Cassandra 是什么以及它可以做什么的教程 但我的问题是如何在 Java 中与 Cassandra 交互 教程会很好 如果可能的话 有人可以告诉我是否应该使用 Thrift 还是 Hector 哪一个更好以及为什
  • 为什么 i++ 不是原子的?

    Why is i Java 中不是原子的 为了更深入地了解 Java 我尝试计算线程中循环的执行频率 所以我用了一个 private static int total 0 在主课中 我有两个线程 主题 1 打印System out prin
  • 制作一个交互式Windows服务

    我希望我的 Java 应用程序成为交互式 Windows 服务 用户登录时具有 GUI 的 Windows 服务 我搜索了这个 我发现这样做的方法是有两个程序 第一个是服务 第二个是 GUI 程序并使它们进行通信 服务将从 GUI 程序获取
  • 无法展开 RemoteViews - 错误通知

    最近 我收到越来越多的用户收到 RemoteServiceException 错误的报告 我每次给出的堆栈跟踪如下 android app RemoteServiceException Bad notification posted fro
  • 多个 Maven 配置文件激活多个 Spring 配置文件

    我想在 Maven 中构建一个环境 在其中我想根据哪些 Maven 配置文件处于活动状态来累积激活多个 spring 配置文件 目前我的 pom xml 的相关部分如下所示
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • 我可以使用 HSQLDB 进行 junit 测试克隆 mySQL 数据库吗

    我正在开发一个 spring webflow 项目 我想我可以使用 HSQLDB 而不是 mysql 进行 junit 测试吗 如何将我的 mysql 数据库克隆到 HSQLDB 如果您使用 spring 3 1 或更高版本 您可以使用 s
  • Java Integer CompareTo() - 为什么使用比较与减法?

    我发现java lang Integer实施compareTo方法如下 public int compareTo Integer anotherInteger int thisVal this value int anotherVal an
  • AWS 无法从 START_OBJECT 中反序列化 java.lang.String 实例

    我创建了一个 Lambda 函数 我想在 API 网关的帮助下通过 URL 访问它 我已经把一切都设置好了 我还创建了一个application jsonAPI Gateway 中的正文映射模板如下所示 input input params
  • 在 Mac 上正确运行基于 SWT 的跨平台 jar

    我一直致力于一个基于 SWT 的项目 该项目旨在部署为 Java Web Start 从而可以在多个平台上使用 到目前为止 我已经成功解决了由于 SWT 依赖的系统特定库而出现的导出问题 请参阅相关thread https stackove
  • 无法捆绑适用于 Mac 的 Java 应用程序 1.8

    我正在尝试将我的 Java 应用程序导出到 Mac 该应用程序基于编译器合规级别 1 7 我尝试了不同的方法来捆绑应用程序 1 日食 我可以用来在 Eclipse 上导出的最新 JVM 版本是 1 6 2 马文 看来Maven上也存在同样的
  • 如何从终端运行处理应用程序

    我目前正在使用加工 http processing org对于一个小项目 但是我不喜欢它附带的文本编辑器 我使用 vim 编写所有代码 我找到了 pde 文件的位置 并且我一直在从 vim 中编辑它们 然后重新打开它们并运行它们 重新加载脚
  • 在mockito中使用when进行模拟ContextLoader.getCurrentWebApplicationContext()调用。我该怎么做?

    我试图在使用 mockito 时模拟 ContextLoader getCurrentWebApplicationContext 调用 但它无法模拟 here is my source code Mock org springframewo
  • 如何从泛型类调用静态方法?

    我有一个包含静态创建方法的类 public class TestClass public static
  • 声明的包“”与预期的包不匹配

    我可以编译并运行我的代码 但 VSCode 中始终显示错误 早些时候有一个弹出窗口 我不记得是什么了 我点击了 全局应用 从那以后一直是这样 Output is there but so is the error The declared
  • 将 List 转换为 JSON

    Hi guys 有人可以帮助我 如何将我的 HQL 查询结果转换为带有对象列表的 JSON 并通过休息服务获取它 这是我的服务方法 它返回查询结果列表 Override public List
  • 如何实现仅当可用内存较低时才将数据交换到磁盘的写缓存

    我想将应用程序生成的数据缓存在内存中 但如果内存变得稀缺 我想将数据交换到磁盘 理想情况下 我希望虚拟机通知它需要内存并将我的数据写入磁盘并以这种方式释放一些内存 但我没有看到任何方法以通知我的方式将自己挂接到虚拟机中before an O
  • 节拍匹配算法

    我最近开始尝试创建一个移动应用程序 iOS Android 它将自动击败比赛 http en wikipedia org wiki Beatmatching http en wikipedia org wiki Beatmatching 两
  • Spring Boot @ConfigurationProperties 不从环境中检索属性

    我正在使用 Spring Boot 1 2 1 并尝试创建一个 ConfigurationProperties带有验证的bean 如下所示 package com sampleapp import java net URL import j

随机推荐

  • 如何经营IT项目中客户关系

    IT项目经理首要的能力是执行计划能力和客户关系能力 在实际工作中 笔者发现许多项目经理具备很好的计划执行能力 却忽视或者不太重视客户关系 给项目实施造成了许多本可以避免的不利因素 影响了项目的进度 甚至造成项目的失败 所以 如何经营好客户关
  • 网站的SEO

    网站的SEO 什么是SEO SEO指search engine optimization 既搜索引擎优化 SEO有什么用 SEO可以提高网站排名 SEO怎么做 内优化 外优化 h1 h2权重比较高 建议使用 因为里面内容容易被检索到 建议只
  • 《FLUENT 14.0超级学习手册》——第2章 FLUENT软件介绍2.1 FLUENT软件特点简介

    本节书摘来自异步社区 FLUENT 14 0超级学习手册 一书中的第2章 第2 1节 作者 唐家鹏 更多章节内容可以访问云栖社区 异步社区 公众号查看 第2章 FLUENT软件介绍 FLUENT 14 0超级学习手册CFD商业软件FLUEN
  • 编译原理实验(词法分析器+语法分析器(递归下降法))

    1 分析C 词法 判断首位为数字的错误变量 通过对C 词法分析程序 GETSYM 的分析 并在此基础上按照教材附录A中给出的C 语言的语法描述 编写一个C 语言的词法分析程序 此程序应具有如下功能 输入为字符串 待进行词法分析的源程序 输出
  • 电脑提示vcruntime140_1.dll丢失怎么安装?

    许多用户在使用电脑的时候 在安装运行某些程序的时候 电脑突然提示由于找不到vcruntime140 1 dll 无法继续执行代码 遇到这种情况我们应该怎么解决呢 下面小编就带着大家一起看看吧 首先是打开电脑浏览器后在顶部输入 dll修复程序
  • 在平面中,一个点绕任意点旋转θ度后的点的坐标

    假设对图片上任意点 x y 绕一个坐标点 rx0 ry0 逆时针旋转a角度后的新的坐标设为 x0 y0 有公式 x0 x rx0 cos a y ry0 sin a rx0 y0 x rx0 sin a y ry0 cos a ry0
  • 网络/Network - 传输层 - UDP

    1 应用场景 主要用于学习计算机网络中传输层的UDP协议 弄清楚其工作原理 2 学习 操作 1 文档阅读 网络协议概论 Laravel 学院 UDP 协议简介 HTTP Socket TCP UDP的区别与联系 网络 Network 数据链
  • 【华为云计算产品系列】云上容灾架构实战部署详解

    华为云计算产品系列 云上容灾架构实战部署详解 1 前言 2 容灾方案介绍 2 1 本地高可用 2 2 同城双活 2 3 主备容灾 同步远程复制 异步远程复制 2 3 1 同步远程复制 2 3 2 异步远程复制 2 4 两地三中心 3 基于主
  • AI 编程助手 Amazon CodeWhisperer 使用(持续更新中)

    文章作者 天博 近期 chatGPT 非常火爆 试用一些公众号功能 及自己接入的 chatGPT 写小作文能力一流 但是准确率很低 一直在搜寻可以在 idea 中使用 且非常丝滑的 AI 编程工具 前不久看到 infoq 推荐的 Amazo
  • Java求出水仙花数

    题目要求 水仙花数必须满足如下2个要求 1 水仙花数是一个三位数 2 水仙花数的个位 十位 百位的数字立方和等于原数 实现思路 1 写出一个100 999的循环 2 将个位 百位和十位求出并计算其平方和 3 判断平方和是否与原数相同 实现代
  • flutter 文字拼接

    Container margin EdgeInsets only right 10 child Row mainAxisSize MainAxisSize min children ConstrainedBox constraints Bo
  • matlab直接在图上加图注,MATLAB insertShape图像上直接添加图像

    官方说明文档 http cn mathworks com help vision ref insertshape html jsessionid cf20e78d9c69962c2a47d7672c89 用figure 和plot的方式无法
  • 行为型模式-状态模式

    package per mjn pattern state after 环境角色类 public class Context 定义对应状态对象的常量 public final static OpeningState OPENING STAT
  • querystring模块

    querystring decode querystring encode querystring escape str querystring parse str sep eq options querystring stringify
  • 作为程序员,你是如何在工作以后找到女朋友的?

    作为程序员 你必然经历这样的一个场景 一个温馨的夜晚 一个可爱懵懂的妹子找你求助 想让你到她家修电脑 如果没有 那么请照镜子检查BUG 一般情况下 场景内会发生三种可能 1 你是一个计算机大牛 从进屋检查到修完电脑再到离开 全程五分钟说拜拜
  • 计算机网络:IP数据报文分析

    IP 数据报前 40 字节 编号 1 45 00 05 dc 4a cb 20 00 40 01 4B 52 c0 a8 00 27 7c 7f cf 0f 08 00 45 2e 00 01 00 17 61 62 63 64 65 66
  • Java 密码学相关知识

    密码的发展 密码的起源 密码的起源非常早 早期的符号密码像宗教符号 岩壁画 至今无人破解的 费托斯圆盘 和无人读懂的 伏尼契手稿 古代隐写术 国内的 封蜡技术 隐形墨水 吞服信条 国外的 卡登格子隐藏法 意大利数学家卡登 两张带孔的卡片 文
  • 在mybatis-plus项目中自带要编写sql语句,@select注解的使用

    提示 在mybatisplus中编写sql语句有两种方法 我重点使用 select注释这种方法 还有一种是增加mapper xml文件配置 这里不做讲解 在mybatis plus项目中自带要编写sql语句 select注解的使用 前言 一
  • Servlet3.0基础

    一 要求 1 MyEclipse10 0或以上版本 2 发布到Tomcat7 0或以上版本 二 步骤 1 创建javaEE6 0应用 三 概述 注解代替web xml配置文件 异步处理 对上传的支持 四 注解代替配置文件 1 删除web x
  • 【java篇】线程安全问题(大总结)

    哎嘿 CSDN的大佬您来啦 这来都来了 浅浅的给个赞呗 系列文章目录 线程的创建与主要方法分析和其他基础知识点 可以参考以下文章 线程知识点总结 南斋孤鹤的博客 CSDN博客 线程知识 超全 线程知识点 及线程方面的一些理解性问题https