【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】多线程(四) 的相关文章

  • Java中字符串中特殊字符的替换

    Java中如何替换字符串 E g String a adf sdf 如何替换和避免特殊字符 您可以删除除此之外的所有字符可打印的 ASCII 范围 http en wikipedia org wiki ASCII ASCII printab
  • 这个函数(for循环)空间复杂度是O(1)还是O(n)?

    public void check 10 for string i list Integer a hashtable get i if a gt 10 hashtable remove i 这是 O 1 还是 O n 我猜测 O n 但不是
  • 正则表达式拆分数字和字母组,不带空格

    如果我有一个像 11E12C108N 这样的字符串 它是字母组和数字组的串联 如何在中间没有分隔符空格字符的情况下分割它们 例如 我希望分割结果为 tokens 0 11 tokens 1 E tokens 2 12 tokens 3 C
  • Java AES 128 加密方式与 openssl 不同

    我们遇到了一种奇怪的情况 即我们在 Java 中使用的加密方法会向 openssl 生成不同的输出 尽管它们在配置上看起来相同 使用相同的键和 IV 文本 敏捷的棕色狐狸跳过了懒狗 加密为 Base64 字符串 openssl A8cMRI
  • JavaFX 中具有自定义内容的 ListView

    How i can make custom ListView with JavaFx for my app I need HBox with image and 2 Labels for each line listView 您可以通过查看
  • 我需要什么库才能在 Java 中访问这个 com.sun.image.codec.jpeg?

    我正在用java创建一个图像水印程序 并导入了以下内容 import com sun image codec jpeg JPEGCodec import com sun image codec jpeg JPEGEncodeParam im
  • 将 SignedHash 插入 PDF 中以进行外部签名过程 -workingSample

    遵循电子书第 4 3 3 节 PDF 文档的数字签名 https jira nuxeo com secure attachment 49931 digitalsignatures20130304 pdf 我正在尝试创建一个工作示例 其中 客
  • 很好地处理数据库约束错误

    再一次 它应该很简单 我的任务是在我们的应用程序的域对象中放置一个具有唯一约束的特定字段 这本身并不是一个很大的挑战 我刚刚做了以下事情 public class Location more fields Column unique tru
  • 编辑文件名在 JComboBox 中的显示方式,同时保持对文件的访问

    我对 Java 很陌生 对堆栈溢出也很陌生 我正在尝试利用 JMF API 创建一个用 Java 编码的简单媒体播放器 到目前为止 我已经能够设置一个简单的队列 播放列表来使用JComboBox called playListHolder
  • Javafx过滤表视图

    我正在尝试使用文本字段来过滤表视图 我想要一个文本字段 txtSearch 来搜索 nhs 号码 名字 姓氏 和 分类类别 我尝试过在线实施各种解决方案 但没有运气 我对这一切仍然很陌生 所以如果问得不好 我深表歉意 任何帮助将不胜感激 我
  • 如何知道抛出了哪个异常

    我正在对我们的代码库进行审查 有很多这样的陈述 try doSomething catch Exception e 但我想要一种方法来知道 doSomething 抛出了哪个异常 在 doSomething 的实现中没有 throw 语句
  • Struts 2 + Sitemesh 3 集成 - FreemarkerDecoratorServlet 中的 NPE

    我将 Struts 2 版本 2 3 14 3 与 Sitemesh 3 版本 3 0 alpha 2 一起使用 并且在某些情况下遇到 NullPointerException 首先 这是我的 web xml 中的 struts2 site
  • java.lang.NumberFormatException: Invalid int: "3546504756",这个错误是什么意思?

    我正在创建一个 Android 应用程序 并且正在从文本文件中读取一些坐标 我在用着Integer parseInt xCoordinateStringFromFile 将 X 坐标转换为整数 Y 坐标的转换方法相同 当我运行该应用程序时
  • 测试弱引用

    在 Java 中测试弱引用的正确方法是什么 我最初的想法是执行以下操作 public class WeakReferenceTest public class Target private String value public Targe
  • Eclipse 中 Spring MVC 模型对象的 (jsp /jstl) 视图中的代码辅助

    在 Spring MVC 中 当将对象放置在视图模型中时 如下所示 public String getUser Model model fetch user model addAttribute user user return viewN
  • 为什么C++代码执行速度比java慢?

    我最近用 Java 编写了一个计算密集型算法 然后将其翻译为 C 令我惊讶的是 C 的执行速度要慢得多 我现在已经编写了一个更短的 Java 测试程序和一个相应的 C 程序 见下文 我的原始代码具有大量数组访问功能 测试代码也是如此 C 的
  • 为什么这个作业不起作用?

    我有课Results which extends ArrayList
  • Trie 数据结构 - Java [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 是否有任何库或文档 链接提供了在 java 中实现 Trie 数据结构的更多信息 任何帮助都会很棒 Thanks 你可以阅读Java特里树
  • 调整添加的绘制组件的大小和奇怪的摆动行为

    这个问题困扰了我好几天 我正在制作一个特殊的绘画程序 我制作了一个 JPanel 并添加了使用 Paint 方法绘制的自定义 jComponent 问题是 每当我调整窗口大小时 所有添加的组件都会 消失 或者只是不绘制 因此我最终会得到一个
  • GUI Java 程序 - 绘图程序

    我一直试图找出我的代码有什么问题 这个想法是创建一个小的 Paint 程序并具有红色 绿色 蓝色和透明按钮 我拥有我能想到的让它工作的一切 但无法弄清楚代码有什么问题 该程序打开 然后立即关闭 import java awt import

随机推荐

  • 链表应用:两数相加

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