【JavaEE】多线程(四)

2023-11-06

多线程(四)

在开始讲之前,我们先来回顾回顾前三篇所讲过的内容~

  1. 线程的概念

    并发编程,多进程,比较重,频繁创建销毁,开销大

  2. Thread的使用

    1. 创建线程
      1. 继承Thread
      2. 实现Runnable
      3. 继承Thread(匿名内部类)
      4. 实现Runnable(匿名内部类)
      5. 使用lambda'
    2. Thread中的重要性
    3. 启动线程start
    4. 终止线程isInterrupted() interrupt()=>本质上是让线程快点执行完入口方法
    5. 等待线程join a.join()让调用这个方法的线程等待a线程的结束
    6. 获取线程引用
    7. 休眠线程
  3. 线程状态(方便快速判定当前程序执行的情况)

    1. NEW
    2. TERMINATED
    3. RUNNABLE
    4. TIMED_WAITING
    5. WAITING
    6. BLOCKED
  4. 线程安全

    1. 演示线程不安全的例子:两个线程自增5w次

    2. 原因:

      • 操作系统对于线程的调度是随机的
      • 多个线程同时修改同一个量
      • 修改操作不是原子性的
      • 内存可见性
      • 指令重排序
    3. 解决:加锁 => synchronized

      synchronized修饰的是一个代码块

      同时指定一个锁对象

      进入代码块的时候,对该对象进行加锁

      出了代码块的时候,对该对象进行解锁


      锁对象

      • 锁对象到底用哪个对象是无所谓的,对象是谁不重要;重要的是两线程加锁的对象是否是同一个对象

      • 这里的意义/规则,有且只有一个

        当两个线程同时尝试对一个对象加锁,此时就会出现“锁冲突”/“锁竞争”,一旦竞争出现,一个线程能够拿到锁,继续执行代码;一个线程拿不到锁,就只能阻塞等待,等待前一个线程释放锁之后,他才有机会拿到锁,继续执行~

      • 这样的规则,本质上就是把“并发执行” => “串行执行”,这样就不会出现“穿插”的情况了。


synchronized 关键字

互斥

续上文最后,synchronized除了修饰代码块之外,还可以修饰一个实例方法,或者一个静态方法

class Counter{
    public int count;

    synchronized public void increase(){
        count++;
    }

    public void increase2(){
        synchronized (this) {
            count++;
        }
    }
    synchronized public static void increase3(){

    }

    public static void increase4(){
        synchronized (Counter.class){

        }
    }
}
// synchtonized 使用方法
public class Demo14 {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Thread t1 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });

        Thread t2 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {
                counter.increase();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(counter.count);
    }
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


synchronized用的锁是存在Java对象头里的。

何为对象头呢?

Java的一个对象,对应的内存空间中,除了你自己定义的一些属性之外,还有一些自带的属性

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在对象头中,其中就会有属性表示当前对象是否已经加锁了


刷新内存

synchronized的工作过程:

  1. 获得互斥锁

  2. 从主内存拷贝变量的最新副本到工作的内存

  3. 执行代码

  4. 将更改后的共享变量的值刷新到主内存

  5. 释放互斥锁

但是目前刷新内存这一块知识各种说法都有,目前也难以通过实例验证,pass~


可重入

synchronized:重要的特性,可重入的

所谓的可重入锁,指的就是,一个线程连续针对一把锁,加锁两次,不会出现死锁。满足这个需求就是“可重入锁”,反之就是“不可重入锁”。

下面见图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上述的现象,很明显就是一个bug,但是我们在日常开发中,又难以避免出现上述的代码~例如下面这样的案例:

public class Demo15 {
    private static Object locker = new Object();

    public static void func1(){
        synchronized (locker){
            func2();
        }
    }

    public  static void func2(){
        func3();
    }

    public static void func3(){
        func4();
    }

    public static void func4(){
        synchronized (locker){

        }
    }

    public static void main(String[] args) {

    }
}

要解决死锁问题,我们可以将synchronized设计成可重入锁,就可以有效解决上述的死锁问题~

就是让锁记录一下,是哪个线程给它锁住的,后续再加锁的时候,如果加锁线程就是持有锁的线程,就直接加锁成功~

用一个例子来理解:

你向一个哥们表白,我爱你,成功了,他接受你了,也就是你对他加锁成功了,同时他也会记得你就是她的男朋友~

过了几天,你又对他说,宝贝我爱你,这时候的那个哥们当然也不会拒绝,反而会更加基情~

不过要是换成别人,结果肯定就是不一样的(排除绿你的情况~)


这里提出个问题:

synchronized(locker){
  synchronized(locker){
  ........................
  }}
  1. 在上述代码中,synchronized是可重入锁,没有因为第二次加锁而死锁,但是当代码执行到 }②,此时锁是否应该释放?

**不能!!!**因为如果释放了锁,很可能就会导致②和①之间的一些代码逻辑无法执行,也就起不到锁保护代码的作用了~

  1. 进一步,如果上述的锁有n层,释放时机该怎么判断?

无论此处有多少层,都是要在最外层才能释放锁~~
引用计数
锁对象中,不光要记录谁拿到了锁,还要记录,锁被加了几次
每加锁一次,计数器就+1.
每解锁一次,计数器就·1.
出了最后一个大括号,恰好就是减成0了,才真正释放锁


死锁

那么上面我们讲解了死锁的一种情况,一个线程针对一把锁,加锁两次。

接下来下面我们继续介绍死锁的情况~

  1. 一个线程针对一把锁,加锁两次,如果是不可重入锁,就会死锁~

    synchronized不会出现,但是隔壁C++的std::mutex就是不可重入锁,就会出现死锁)

  2. 两个线程(t1、t2),两把锁(A、B)(此时无论是不是不可重入锁,都会死锁)

    举个例子:钥匙锁车里,车钥匙锁家里~

    1. t1获取锁A,t2获取锁B
    2. t1尝试获取B,t2尝试获取A

    实例代码

    // 死锁
    public class Demo16 {
        private static Object locker1 = new Object();
        private static Object locker2 = new Object();
    
    //此处的sleep很重要,要确保 t1 和 t2 都分别拿到一把锁之后,再进行后续动作
        public static void main(String[] args) {
         Thread t1 = new Thread(()->{
             synchronized (locker1){
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
    
                 synchronized (locker2){
                     System.out.println("t1 加锁成功");
                 }
             }
    
         });
         Thread t2 = new Thread(()->{
             synchronized (locker2){
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 synchronized (locker1){
                     System.out.println("t2 加锁成功");
                 }
             }
         });
         t1.start();
         t2.start();
        }
    }
    

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    死锁现象出现

    我们可以在jconsole.exe中看看线程情况~

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    同时也要注意,死锁代码中
    两个synchronized嵌套关系,不是并列关系.
    嵌套关系说明:是在占用一把锁的前提下,获取另一把锁.(则是可能出现死锁)
    并列关系,则是先释放前面的锁,再获取下一把锁.(不会死锁的)

// 死锁 -> 破嵌套
public class Demo17 {
    private static Object locker1 = new Object();
    private static Object locker2 = new Object();

    //此处的sleep很重要,要确保 t1 和 t2 都分别拿到一把锁之后,再进行后续动作
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            synchronized (locker1){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
                synchronized (locker2){
                    System.out.println("t1 加锁成功");
                }
        });
        Thread t2 = new Thread(()->{
            synchronized (locker2) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
                synchronized (locker1){
                    System.out.println("t2 加锁成功");
                }

        });
        t1.start();
        t2.start();
    }
}
// 破死锁
public class Demo18 {
    private static Object locker1 = new Object();
    private static Object locker2 = new Object();

    //此处的sleep很重要,要确保 t1 和 t2 都分别拿到一把锁之后,再进行后续动作
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            synchronized (locker1){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (locker2){
                System.out.println("t1 加锁成功");
            }
        });
        Thread t2 = new Thread(()->{
            synchronized (locker1) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            synchronized (locker2){
                System.out.println("t2 加锁成功");
            }

        });
        t1.start();
        t2.start();
    }
}

第一段代码中使用的是同一个对象作为锁,在t1和t2线程中都使用了locker对象作为锁。这样的话,当t1线程获取到锁并休眠时,t2线程就无法获取到锁,导致t2线程一直等待,从而可能引发死锁。

第二段代码中使用了两个不同的对象作为锁,分别是locker1locker2。在t1线程中先获取locker1锁,再获取locker2锁;在t2线程中先获取locker2锁,再获取locker1锁。这样的话,两个线程在互斥的同时也保持了顺序,避免了死锁的发生。

  1. N个线程,M把锁(相当于2的扩充)

    此时这个情况,更加容易出现死锁了。

    下面给出一个经典例子:哲学家就餐问题

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    死锁,是属于比较严重的bug,会直接导致线程卡住,也就无法执行后续的工作了~

    那么我们应该怎么避免死锁?

死锁的成因

那么首先我们要了解死锁的成因:

  1. 互斥使用。(锁的基本特性)

    当线程持有一把锁之后,另一个线程也想获取到锁,那么就需要阻塞等待、

  2. 不可抢占。(锁的基本特性)

    当锁已经被 线程 1 拿到之后,线程 2 只能等 线程 1 主动释放,不可以强行抢过来

  3. 请求保持。(代码结构)

    一个线程尝试获取多把锁。(先拿到 锁1 之后,再尝试获取 锁2 ,获取的时候, 锁1 不会被释放)

    这种也就是典型的吃着碗里的,看着锅里的

    public class Demo16 {
        private static Object locker1 = new Object();
        private static Object locker2 = new Object();
    
    //此处的sleep很重要,要确保 t1 和 t2 都分别拿到一把锁之后,再进行后续动作
        public static void main(String[] args) {
         Thread t1 = new Thread(()->{
             synchronized (locker1){
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
    
                 synchronized (locker2){
                     System.out.println("t1 加锁成功");
                 }
             }
    
         });
         Thread t2 = new Thread(()->{
             synchronized (locker2){
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 synchronized (locker1){
                     System.out.println("t2 加锁成功");
                 }
             }
         });
         t1.start();
         t2.start();
        }
    }
    
  4. 循环等待 / 环路等待(代码结构)

    等待的依赖关系,形成环了~

    也即是上面那个例子,钥匙锁车里,车钥匙锁家里

实际上,要想出现死锁,也不是个容易事情
因为得把上面4条都占了.
(不幸的是,1和2是锁本身的特性,只要代码中,把3和4占了,死锁就容易出现了)

所以说,解决死锁,核心就是破坏上述必要条件,死锁就形成不了~

针对上述的四种成因,1 2是破坏不了的,因为synchronized自带特性,我们是无法干预 滴~

对于3来说,就是调整代码结构,避免编写“锁嵌套”逻辑

对于4来说,可以约定加锁的顺序,就可以避免循环等待


所以针对上面的哲学家就餐问题,我们可以采取:针对锁进行编号

比如说约定,加多一把锁的时候,先加编号小的锁,后加编号大的锁(所有线程都要遵守这个规则)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这样的话,循环等待就会被解除,死锁也不会出现了~

回到上述我们讲的synchronized关键字
在使用规则上,并不复杂,只要抓住一个原则:两个线程针对同一个对象加锁,就会产生锁竞争.
但是在底层原理上,synchronized还有不少值得讨论的地方.接下来会展开讲讲~


至此,多线程(四)讲解到这,接下来会持续更新,敬请期待~

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

【JavaEE】多线程(四) 的相关文章

  • Hashmap并发问题

    我有一个哈希图 出于速度原因 我希望不需要锁定 假设我不介意过时的数据 同时更新它和访问它会导致任何问题吗 我的访问是获取 而不是迭代 删除是更新的一部分 是的 这会导致重大问题 一个例子是向散列映射添加值时可能发生的情况 这可能会导致表重
  • 位图内存不足错误

    我对这个错误有疑问 我从 URL 制作网站图标解析器 我这样做是这样的 public class GrabIconsFromWebPage public static String replaceUrl String url StringB
  • jvm 次要版本与编译器次要版本

    当运行使用具有相同主要版本但次要版本高于 JVM 的 JDK 编译的类时 JVM 会抛出异常吗 JDK 版本并不重要 类文件格式版本 http blogs oracle com darcy entry source target class
  • 防止 Spring Boot 注册 Spring Security 过滤器之一

    我想禁用安全链中的 Spring Security 过滤器之一 我已经看到了防止 Spring Boot 注册 servlet 过滤器 https stackoverflow com questions 28421966 prevent s
  • URL.setURLStreamHandlerFactory

    我正在使用带有嵌入式 Jetty 的可执行 jar 开发一个 Web 应用程序 我的jar包含一个依赖jar jar in jar 我参考了JarRsrcLoader and RsrcURLStreamHandlerFactory由 Ecl
  • 在哪里可以获得有关 Java FitNesse 和 Slim 的一些教程? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 如何开始使用 Chainsaw for Log4j?

    我想开始使用 Chainsaw v2 几乎没有关于它的信息 我只找到了this http www velocityreviews com forums t140105 help using chainsaw for log4j html 但
  • 使用 ChannelExec 的命令未执行 - Jsch

    我正在使用 Jsch 在服务器中创建一个文件并执行一些命令 对于文件创建 它工作正常 但是对于命令执行 则不然 它保持状态 1 仍在处理它 并永远保持该状态 这种情况发生在 shell 执行或我尝试成为 root 时 请按照以下方法操作 p
  • java.lang.LinkageError:尝试重复的类定义

    为什么会发生错误以及如何修复它 02 13 02 pool 4 thread 2 WARN Exception in thread pool 4 thread 2 02 13 02 pool 4 thread 2 WARN java lan
  • 正则表达式获取字符串中的第一个数字和其他字符

    我是正则表达式的新手 想知道如何才能只获取字符串中的第一个数字 例如100 2011 10 20 14 28 55 在这种情况下 我希望它返回100 但该数字也可以更短或更长 我在想类似的事情 0 9 但它单独获取每个数字 100 2001
  • 字符串池可以包含两个具有相同值的字符串吗? [复制]

    这个问题在这里已经有答案了 字符串池可以包含两个具有相同值的字符串吗 String str abc String str1 new String abc Will the second statement with new operator
  • 如何在 Eclipse 中使用其他外部 jar 依赖项创建不可运行/不可执行的 jar

    我无法通过 Eclipse 导出向导创建普通的 jar 不可运行 不可执行 它仅创建 jar 文件 但不会导出依赖的 jar 从而在从其他类调用导出的 jar 的方法时出现错误 请帮助 非常感谢 kurellajunior的建议 它是通过使
  • 发生错误。请参阅日志文件 - eclipse juno

    每当我启动 Eclipse Juno 时 都会出现错误 发生错误 请查看日志文件 C Program Files eclipse configuration 1362989254411 log 有的网站说卸载jdk重新安装 我这样做了 但没
  • Java:java.util.ConcurrentModificationException

    我正在制作 2D 目前正在研究用子弹射击 子弹是一个单独的类 所有项目符号都存储在称为项目符号的数组列表中 当它超出屏幕一侧 Exception in thread main java util ConcurrentModification
  • 如何找到被点击的JLabel并从中显示ImageIcon?

    这是我的代码 我想知道哪个l单击 然后在新框架中显示该 ImageIcon e getSource 不起作用 final JFrame shirts new JFrame T shirts JPanel panel new JPanel n
  • JSch中如何设置文件类型和文件传输模式?

    我使用 Apache Common NetFTPClient并设置了我的ftpClient在上传文件之前使用如下所示的方法 ftpClient setFileType FTP BINARY FILE TYPE ftpClient setFi
  • 了解 Spark 中的 DAG

    问题是我有以下 DAG 我认为当需要洗牌时 火花将工作划分为不同的阶段 考虑阶段 0 和阶段 1 有些操作不需要洗牌 那么为什么 Spark 将它们分成不同的阶段呢 我认为跨分区的实际数据移动应该发生在第 2 阶段 因为这里我们需要cogr
  • 公共方法与公共 API

    在干净的代码书中 有一个观点是 公共 API 中的 Javadocs 同样 Effective java 一书也有这样的内容 项目 56 为所有公开的 API 元素编写文档注释 所以这就是我的问题 所有公共方法都被视为公共 API 吗 它们
  • 摩尔斯电码 至 英语

    我现在的问题是让 摩尔斯电码转英语 正常工作 将英语转换为莫尔斯电码的第一部分工作正常 我知道以前已经有人问过这个问题 但我不知道我做错了什么 我知道我需要在某个地方进行拆分 但我只是不确定将其放在代码中的何处 现在 莫尔斯电码到英语的部分
  • 防止Java实例化的正确方法[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi

随机推荐

  • 链表应用:两数相加

    关于链表 链表是一种极其重要的数据结构 因为对指针和抽象思维的要求较高 一度成为身边同学最痛恨的对象 我在将这里演示如何使用链表制作一个可以按位储存数字的容器 鉴于本人亦初学者 有错误请各位在评论区指正 这里还是以介绍链表为主 算法部分苦于
  • SpringCloud和微服务介绍

    SpringCloud介绍 微服务架构是什么 微服务实例的开发 服务的注册与发现 负载均衡 服务容错 API网关 分布式配置中心 调试 部署 持续集成 SpringCloud介绍 SpringCloud是在SpringBoot的基础上构建的
  • 一个完整详细的二维SVR案例分析过程

    文章目录 案例介绍 数据预处理 函数拟合仿真 SVR建模 模型调参 案例介绍 首先 此次案例是以油气开发为背景 选取加粗样式其中重要的两个参数含油饱和度和孔隙度分别作为此次案例的自变量和因变量进行试验 按照正常的案例分析步骤进行操作 此次为
  • 神经网络(ANN)

    算法介绍 概念 人工神经网络是由具有适应性的简单单元组成的广泛并行互连的网络 它的组织能够模拟生物神经神经系统对真实世界物体所作出的交互反应 在实际应用中 80 90 的人工神经网络模型是采用误差反转算法或其变形形式的网络模型 一个神经网络
  • 小程序的配置文件和小程序的模板语法

    微信小程序 小程序的配置文件 一个小程序应用程序会包括 会有最基本的两种配置文件 一种是全局的 app json 一种是页面自己的 page json 注意 配置文件中不可以出行注释 1 1 全局配置文件 app json app json
  • 主题模型(Topic Model)与LDA算法

    Topic Model 主题模型 Topic Model 是以非监督学习的方式对文档的隐含语义结构 latent semantic structure 进行聚类 clustering 的统计模型 主题模型认为在词 word 与文档 docu
  • 第十三章:QT多线程(QThread)

    回顾 第一章 Qt的概述 第二章 在Ubuntu编写第一个Qt程序 第三章 Qt的字符串和字符编码 第四章 Qt的信号和槽 第五章 Qt容器窗口 父窗口 第六章 面向对象的Qt编程 第七章 Qt设计师使用 designer 第八章 Qt创造
  • Unittest框架多个testcase之间全局变量的调用

    unittest模块进行接口自动化的时候遇到以下场景 新增 修改 查看 删除一个项目配置流程 但是每次用真实数据请求 数据库就会增加很多脏数据 所以就产生了数据一条龙服务 从新增到删除 涉及到了Unittest下个接口需要调用上个接口的返回
  • mysql数据库访问控制查询_MySQL ------ 管理用户对数据库的访问控制(GRANT 与 REVOKE)(二十九)...

    数据库服务器通常包含着关键的数据 所以为了确保这些数据的安全和完整需要要利用访问控制 MySQL服务器的安全基础是 用户应该对他们需要的数据具有适当的访问权 既不能多也不能少 即用户不能对过多的数据具有访问权 访问控制 需要给用户提供他们所
  • 电子计算机的发展史

    1 电子计算机元器件变化 继电器 真空管 晶体管 2 计算机的出现背景 20世纪人口暴增 科学与工程进步迅速 航天计划成形 以上导致数据的复杂度急剧上升 计算量暴增 对于计算的自动化 高速有迫切的需求 3 电子计算机的发展 1945年 哈佛
  • antd table合并行或者列(动态添加合并行、列)

    antd table合并行或者列 动态添加合并行 列 表头只支持列合并 使用 column 里的 colSpan 进行设置 表格支持行 列合并 使用 render 里的单元格属性 colSpan 或者 rowSpan 设值为 0 时 设置的
  • Ubuntu系统安装企业微信和微信

    一 Ubuntu16 04安装企业微信 1 安装git工具 sudo apt install wget g git 2 安装deepin wine git clone https gitee com wszqkzqk deepin wine
  • UE4 行为树装饰器(Decorator)简单实用说明(1)

    在行为树标准模型中 条件语句为 Task 叶节点 除成功和失败外不执行任何操作 虽然也可以使用传统的条件 tasks 但还是强烈推荐您使用 Decorator 系统作为条件语句 使用 decorators 而非 tasks 作为条件语句有多
  • 光猫超级账号密码、宽带账号密码 获取

    光猫超级账号密码 宽带账号密码 获取 目的与起因 租房宽带是电信的 Ipv6 之前笔记本使用 Ipv6 地址可以直接被手机通过移动网络无需中转进行远程桌面连接的 目前尝试了在局域网内是可以实现的 应该是电信屏蔽了3389端口 在光猫路由器修
  • tcp 序列号

    父 tcp 状态 from异常流程 个人渣记录仅为自己搜索用的博客 CSDN博客 转载请注明出处 6 TCP6 TCP 协议 序号和确认号 tcp 最终确认序号 Allen 的博客 CSDN博客6 TCP 接下来的内容是学习后续内容的基础
  • Python Pandas 数据拼接/排序/重置

    Pandas 数据拼接 排序 重置 一 数据拼接 1 1 行拼接 纵向 第 0 维 pd concat df1 df2 1 2 列拼接 横向 第 1 维 pd concat df1 df2 axis 1 二 排序 2 1 从小到大排序 默认
  • CMake设置MSVC工程MT/MTd/MD/MDd

    文章目录 0 前言 1 如何设置 1 1 CMakeLists代码 1 2 要点1 POLICY 1 3 要点2 set property 0 前言 在MSVC工程上右键 gt 属性 找到配置属性 gt C C gt 代码生成 gt 运行库
  • 设计模式精讲-抽象工厂方法模式

    设计模式 抽象工厂方法模式 定义 示例 应用场景 优点 定义 提供一个创建一系列相关或互相依赖对象的接口 而无需指定它们具体的类 定义和图不理解的 可以先看下面的示例 回头再去理解 示例 以数据库为例 1 变化的部分 Mysq Oracle
  • 在LDAP中使用角色(Role)和组(Group)来管理用户

    LDAP 轻量级目录服务器 越来越被广泛的使用 特别是在管理海量用户信息和管理身份认证信息的时候 LDAP被国内大多数企业所使用 从中国电信 中 国移动 新浪 和许多省市政府部门都使用LDAP来管理用户身份的信息 下面重点介绍在LDAP中管
  • 【JavaEE】多线程(四)

    多线程 四 在开始讲之前 我们先来回顾回顾前三篇所讲过的内容 线程的概念 并发编程 多进程 比较重 频繁创建销毁 开销大 Thread的使用 创建线程 继承Thread 实现Runnable 继承Thread 匿名内部类 实现Runnabl