【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篇】线程安全问题(大总结) 的相关文章

  • 用于解析和构建逻辑表达式的 Java 库

    我正在寻找一个 Java 开源库来解析和构建类似 SQL 的表达式 例如评估表达式的有效性 例如 a x or y and b z 另外我想要一个用于构建或扩展表达式的 API 就像是 Expression exp new Expressi
  • 将 MouseListener 添加到面板

    我正在尝试将鼠标操作添加到我的面板中 这就是程序应该做的事情 编写一个程序 允许用户通过按三下鼠标来指定一个三角形 第一次按下鼠标后 画一个小点 第二次按下鼠标后 绘制一条连接前两个点的线 第三次按下鼠标后 绘制整个三角形 第四次按下鼠标会
  • 查询 MongoDB 集合中的字段。

    我正在尝试查询 mongodb 集合中的特定字段 这是我的代码和输出 Mongo m new Mongo DB db m getDB mydb DBCollection coll db getCollection student addin
  • 使用 Spring 控制器处理错误 404

    I use ExceptionHandler处理我的网络应用程序抛出的异常 在我的例子中我的应用程序返回JSON回应HTTP status用于对客户端的错误响应 但是 我正在尝试弄清楚如何处理error 404返回与处理的类似的 JSON
  • 这个等待通知线程语义的真正目的是什么?

    我刚刚遇到一些代码 它使用等待通知构造通过其其他成员方法与类中定义的线程进行通信 有趣的是 获取锁后 同步范围内的所有线程都会在同一锁上进行定时等待 请参见下面的代码片段 随后 在非同步作用域中 线程执行其关键函数 即 做一些有用的事情1
  • 如何在 MSSQL 中获取 CURRENT_DATE?

    我正在使用 jpa 3 o 和 Hibernate 我有一个命名查询 SELECT COUNT wt id FROM WPSTransaction wt WHERE wt createdDate gt CURRENT DATE WPSTra
  • JavaFX使节点覆盖父节点边框颜色

    我有一个如下所示的节点 仅使用 css 我希望标签覆盖其父边框颜色 因此标签下方的边框颜色部分变得不可见 我用来制作这个边框的CSS代码 fx border color black fx border width 3 fx border r
  • 如何在 HandlerInterceptorAdapter 中添加 HttpServletRequest 标头?

    我正在尝试将授权标头添加到我的请求中 作为我们切换环境时的临时解决方法 我试图在扩展 HandlerInterceptorAdapter 的拦截器中处理它 我使用 MutableHttpServletRequest 类制作here http
  • 确定序列化对象的类型

    我需要通过套接字发送消息 从用户到引擎的请求 以及从引擎到用户的响应 所以流程本质上是 serialized request Server lt network gt Client serialized response request r
  • Struts 1 到 Spring 迁移 - 策略

    我有一个legacy银行应用程序编码为Struts 1 JSP现在的要求是迁移后端 目前为 MVC to Springboot MVC 后续UI JSP 将迁移到angular Caveats 1 后端不是无状态的 2 会话对象中存储了大量
  • 纱线上的火花,连接到资源管理器 /0.0.0.0:8032

    我正在我的开发机器 Mac 上编写 Spark 程序 hadoop的版本是2 6 spark的版本是1 6 2 hadoop集群有3个节点 当然都在linux机器上 我在idea IDE中以spark独立模式运行spark程序 它运行成功
  • 在带有 Protocol Buffers 的项目中使用 Proguard 有什么特点?

    我有一个使用 Google Protocol Buffers 的项目 一旦我尝试用 ProGuard 对其进行混淆 似乎 protobuf 会导致问题 我将所有自己的类打包成mybuildedclasses jar 谷歌代码被打包成prot
  • 如何从intellij项目视图中隐藏不必要的文件?

    给定一个示例 gradle 项目 其项目结构如下所示 正如你所看到的 有很多东西你实际上不需要在想法中看到 但你需要它们存在 我知道下面被忽略的文件 文件夹类型Editor File Types但这些正在影响库和项目 idea 会在各处忽略
  • Java 中 JButton 的击键/热键

    最初我使用 JMenu 并建立热键以使用加速器工作 它运行得很好 现在我想在 JButton 中实现相同的行为 但我陷入困境 这是我编写的代码 请分享您的想法 以便我可以走上正确的道路 import javax swing import j
  • 如何以编程方式创建 CardView

    我正在开发一个 Android 应用程序Java Android Studio 我想在活动中创建CardView以编程方式 我想将以下属性设置为CardView layout width wrap content layout row 0
  • 如何使用Gson仅从Json反序列化某些特定字段?

    我有以下 JSON 字符串 channel bvmt initValues data value instrumentIds TN0007250012 TN0007500010 instruments mnemonic ADWYA marc
  • java中使用多线程调用同一类的不同方法

    我有一个类 如下所示 具有三种方法 public class MyRunnable implements Runnable Override public void run what code need to write here to c
  • spring data jpa复合键重复键记录插入导致更新

    我有一个具有复合键的实体 我试图通过使用 spring data jpa 存储库到 mysql 数据库来持久化它 如下所示 Embeddable public class MobileVerificationKey implements S
  • 为什么java.lang.Cloneable不重写java.lang.Object中的clone()方法?

    Java 规范java lang Cloneable接口将自身定义为表示扩展它的任何对象也实现了clone 休眠的方法java lang Object 具体来说 它说 一个类实现了Cloneable接口来指示java lang Object
  • Errors/BindingResult 参数应在模型属性、@RequestBody 或 @RequestPart 参数之后立即声明

    我通过剖析示例应用程序来自学 Spring 然后到处添加代码来测试我在剖析过程中开发的理论 在测试添加到 Spring 应用程序中的一些代码时 我收到以下错误消息 An Errors BindingResult argument is ex

随机推荐

  • 如何经营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