本站提供最佳integer源码详解服务,欢迎转载和分享。

【视频网站源码app】【线上投票源码】【看源码需要英文】java 线程 源码_java线程源码

2024-11-27 03:47:22 来源:解析源码VIPcookie 分类:综合

1.JAVA如何获取jvm中的线程线程所有线程?
2.Java的并行世界-Netty中线程模型源码讲解-续集Handler、Channel
3.Java并发编程笔记之LinkedBlockingQueue源码探究
4.JAVA 线程
5.Java多线程——singleThreadExecutor

java 线程 源码_java线程源码

JAVA如何获取jvm中的源码源码所有线程?

       在Java中,你可以通过Java的线程线程java.lang.management包获取JVM中的所有线程。这个包提供了一些用于管理和监视Java虚拟机的源码源码工具。具体来说,线程线程你可以使用ThreadMXBean接口来获取线程信息。源码源码视频网站源码app

       以下是线程线程一段示例代码,演示如何获取和打印JVM中的源码源码所有线程:

       java复制代码

       import java.lang.management.ManagementFactory;

       import java.lang.management.ThreadInfo;

       import java.lang.management.ThreadMXBean;

       public class Main {

       public static void main(String[] args) {

       // 获取ThreadMXBean

       ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();

       // 不需要获取同步的monitor和synchronizer信息,仅获取线程和线程堆栈信息

       ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false,线程线程 false);

       // 遍历线程信息,仅打印线程ID和线程名称信息

       for (ThreadInfo threadInfo : threadInfos) {

       System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName());

       }

       }

       }

       这段代码首先通过ManagementFactory.getThreadMXBean()获取ThreadMXBean实例,源码源码然后调用dumpAllThreads()方法获取所有线程的线程线程ThreadInfo,最后遍历并打印所有线程的源码源码ID和名称。

       需要注意的线程线程是,dumpAllThreads()方法会返回一个ThreadInfo数组,源码源码每个ThreadInfo代表一个线程,线程线程线上投票源码包含了关于该线程的大量信息,包括线程ID、线程名称、线程状态、线程堆栈信息等。在上面的示例代码中,我们只打印了线程ID和线程名称,但你可以根据需要打印其他信息。

Java的并行世界-Netty中线程模型源码讲解-续集Handler、Channel

       Netty 的核心组件 ChannelHandler 在网络应用中扮演着关键角色,它处理各种事件和数据,实现业务逻辑。ChannelHandler 子类众多,根据功能可分为特殊Handler(如Context对象)、看源码需要英文出入站Handler,以及用于协议解析和编码的Decoder和Encoder。例如,ChannelInboundHandlerAdapter 和 ChannelOutboundHandlerAdapter 分别用于处理入站和出站事件,ByteToMessageDecoder 和 MessageToByteEncoder 则负责数据的解码和编码。

       特殊Handler如ChannelHandlerContext 提供了处理器与Channel交互的上下文,而ChannelDuplexHandler 则用于双向通信,如聊天服务器。SimpleChannelInboundHandler 是简化版的入站处理器,自动管理消息引用,避免内存泄漏。而出站处理器如SimpleChannelOutboundHandler 则在消息处理后自动释放引用,简化编码流程。

       Channel 是贷超渠道源码数据传输的抽象,NioServerSocketChannel 和 EpollServerSocketChannel 分别对应基于NIO和Epoll的服务器端套接字。ChannelInitializer 是初始化新Channel的关键,它配置处理器形成处理链,用于处理连接操作和事件,从而实现自定义业务逻辑。

       通过理解这些概念和类的作用,可以构建和配置Netty应用,以满足不同的网络通信需求。想要深入学习,可以研究Netty 4.1源码中如EventLoopGroup、ChannelPipeline、CustomChannelInitializer等核心类。后续会分享详细的中文注释版本,持续关注以获取更多资源和知识。dashboard前端源码解析

Java并发编程笔记之LinkedBlockingQueue源码探究

       LinkedBlockingQueue 是基于单向链表实现的一种阻塞队列,其内部包含两个节点用于存放队列的首尾,并维护了一个表示元素个数的原子变量 count。同时,它利用了两个 ReentrantLock 实例(takeLock 和 putLock)来保证元素的原子性入队与出队操作。此外,notEmpty 和 notFull 两个信号量与条件队列用于实现阻塞操作,使得生产者和消费者模型得以实现。

       LinkedBlockingQueue 的实现主要依赖于其内部锁机制和信号量管理。构造函数默认容量为最大整数值,用户可自定义容量大小。offer 方法用于尝试将元素添加至队列尾部,若队列未满则成功,返回 true,反之返回 false。若元素为 null,则抛出 NullPointerException。put 方法尝试将元素添加至队列尾部,并阻塞当前线程直至队列有空位,若被中断则抛出 InterruptedException。通过使用 putLock 锁,确保了元素的原子性添加以及元素计数的原子性更新。

       在实现细节上,offer 方法通过在获取 putLock 的同时检查队列是否已满,避免了不必要的元素添加。若队列未满,则执行入队操作并更新计数器,同时考虑唤醒等待队列未满的线程。此过程中,通过 notFull 信号量与条件队列协调线程间等待与唤醒。

       put 方法则在获取 putLock 后立即检查队列是否满,若满则阻塞当前线程至 notFull 信号量被唤醒。在入队后,更新计数器,并考虑唤醒等待队列未满的线程,同样通过 notFull 信号量实现。

       poll 方法用于从队列头部获取并移除元素,若队列为空则返回 null。此方法通过获取 takeLock 锁,保证了在检查队列是否为空和执行出队操作之间的原子性。在出队后,计数器递减,并考虑激活因调用 poll 或 take 方法而被阻塞的线程。

       peek 方法类似,但不移除队列头部元素,返回 null 若队列为空。此方法也通过获取 takeLock 锁来保证操作的原子性。

       take 方法用于阻塞获取队列头部元素并移除,若队列为空则阻塞当前线程直至队列不为空。此方法与 put 方法类似,通过 notEmpty 信号量与条件队列协调线程间的等待与唤醒。

       remove 方法用于移除并返回指定元素,若存在则返回 true,否则返回 false。此方法通过双重加锁机制(fullyLock 和 fullyUnlock)来确保元素移除操作的原子性。

       size 方法用于返回当前队列中的元素数量,通过 count.get() 直接获取,确保了操作的准确性。

       综上所述,LinkedBlockingQueue 通过其独特的锁机制和信号量管理,实现了高效、线程安全的阻塞队列操作,适用于生产者-消费者模型等场景。

JAVA 线程

       è¿™æ˜¯javaeye上非常经典的关于线程的帖子,写的非常通俗易懂的,适合任何读计算机的同学.

       çº¿ç¨‹åŒæ­¥

       æˆ‘们可以在计算机上运行各种计算机软件程序。每一个运行的程序可能包括多个独立运行的线程(Thread)。

       çº¿ç¨‹ï¼ˆThread)是一份独立运行的程序,有自己专用的运行栈。线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。

       å½“多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。

       åŒæ­¥è¿™ä¸ªè¯æ˜¯ä»Žè‹±æ–‡synchronize(使同时发生)翻译过来的。我也不明白为什么要用这个很容易引起误解的词。既然大家都这么用,咱们也就只好这么将就。

       çº¿ç¨‹åŒæ­¥çš„真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

       å› æ­¤ï¼Œå…³äºŽçº¿ç¨‹åŒæ­¥ï¼Œéœ€è¦ç‰¢ç‰¢è®°ä½çš„第一点是:线程同步就是线程排队。同步就是排队。线程同步的目的就是避免线程“同步”执行。这可真是个无聊的绕口令。

       å…³äºŽçº¿ç¨‹åŒæ­¥ï¼Œéœ€è¦ç‰¢ç‰¢è®°ä½çš„第二点是 “共享”这两个字。只有共享资源的读写访问才需要同步。如果不是共享资源,那么就根本没有同步的必要。

       å…³äºŽçº¿ç¨‹åŒæ­¥ï¼Œéœ€è¦ç‰¢ç‰¢è®°ä½çš„第三点是,只有“变量”才需要同步访问。如果共享的资源是固定不变的,那么就相当于“常量”,线程同时读取常量也不需要同步。至少一个线程修改共享资源,这样的情况下,线程之间就需要同步。

       å…³äºŽçº¿ç¨‹åŒæ­¥ï¼Œéœ€è¦ç‰¢ç‰¢è®°ä½çš„第四点是:多个线程访问共享资源的代码有可能是同一份代码,也有可能是不同的代码;无论是否执行同一份代码,只要这些线程的代码访问同一份可变的共享资源,这些线程之间就需要同步。

       ä¸ºäº†åŠ æ·±ç†è§£ï¼Œä¸‹é¢ä¸¾å‡ ä¸ªä¾‹å­ã€‚

       æœ‰ä¸¤ä¸ªé‡‡è´­å‘˜ï¼Œä»–们的工作内容是相同的,都是遵循如下的步骤:

       ï¼ˆ1)到市场上去,寻找并购买有潜力的样品。

       ï¼ˆ2)回到公司,写报告。

       è¿™ä¸¤ä¸ªäººçš„工作内容虽然一样,他们都需要购买样品,他们可能买到同样种类的样品,但是他们绝对不会购买到同一件样品,他们之间没有任何共享资源。所以,他们可以各自进行自己的工作,互不干扰。

       è¿™ä¸¤ä¸ªé‡‡è´­å‘˜å°±ç›¸å½“于两个线程;两个采购员遵循相同的工作步骤,相当于这两个线程执行同一段代码。

       ä¸‹é¢ç»™è¿™ä¸¤ä¸ªé‡‡è´­å‘˜å¢žåŠ ä¸€ä¸ªå·¥ä½œæ­¥éª¤ã€‚采购员需要根据公司的“布告栏”上面公布的信息,安排自己的工作计划。

       è¿™ä¸¤ä¸ªé‡‡è´­å‘˜æœ‰å¯èƒ½åŒæ—¶èµ°åˆ°å¸ƒå‘Šæ çš„前面,同时观看布告栏上的信息。这一点问题都没有。因为布告栏是只读的,这两个采购员谁都不会去修改布告栏上写的信息。

       ä¸‹é¢å¢žåŠ ä¸€ä¸ªè§’色。一个办公室行政人员这个时候,也走到了布告栏前面,准备修改布告栏上的信息。

       å¦‚果行政人员先到达布告栏,并且正在修改布告栏的内容。两个采购员这个时候,恰好也到了。这两个采购员就必须等待行政人员完成修改之后,才能观看修改后的信息。

       å¦‚果行政人员到达的时候,两个采购员已经在观看布告栏了。那么行政人员需要等待两个采购员把当前信息记录下来之后,才能够写上新的信息。

       ä¸Šè¿°è¿™ä¸¤ç§æƒ…况,行政人员和采购员对布告栏的访问就需要进行同步。因为其中一个线程(行政人员)修改了共享资源(布告栏)。而且我们可以看到,行政人员的工作流程和采购员的工作流程(执行代码)完全不同,但是由于他们访问了同一份可变共享资源(布告栏),所以他们之间需要同步。

       åŒæ­¥é”

       å‰é¢è®²äº†ä¸ºä»€ä¹ˆè¦çº¿ç¨‹åŒæ­¥ï¼Œä¸‹é¢æˆ‘们就来看如何才能线程同步。

       çº¿ç¨‹åŒæ­¥çš„基本实现思路还是比较容易理解的。我们可以给共享资源加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资源。

       ç”Ÿæ´»ä¸­ï¼Œæˆ‘们也可能会遇到这样的例子。一些超市的外面提供了一些自动储物箱。每个储物箱都有一把锁,一把钥匙。人们可以使用那些带有钥匙的储物箱,把东西放到储物箱里面,把储物箱锁上,然后把钥匙拿走。这样,该储物箱就被锁住了,其他人不能再访问这个储物箱。(当然,真实的储物箱钥匙是可以被人拿走复制的,所以不要把贵重物品放在超市的储物箱里面。于是很多超市都采用了电子密码锁。)

       çº¿ç¨‹åŒæ­¥é”è¿™ä¸ªæ¨¡åž‹çœ‹èµ·æ¥å¾ˆç›´è§‚。但是,还有一个严峻的问题没有解决,这个同步锁应该加在哪里?

       å½“然是加在共享资源上了。反应快的读者一定会抢先回答。

       æ²¡é”™ï¼Œå¦‚果可能,我们当然尽量把同步锁加在共享资源上。一些比较完善的共享资源,比如,文件系统,数据库系统等,自身都提供了比较完善的同步锁机制。我们不用另外给这些资源加锁,这些资源自己就有锁。

       ä½†æ˜¯ï¼Œå¤§éƒ¨åˆ†æƒ…况下,我们在代码中访问的共享资源都是比较简单的共享对象。这些对象里面没有地方让我们加锁。

       è¯»è€…可能会提出建议:为什么不在每一个对象内部都增加一个新的区域,专门用来加锁呢?这种设计理论上当然也是可行的。问题在于,线程同步的情况并不是很普遍。如果因为这小概率事件,在所有对象内部都开辟一块锁空间,将会带来极大的空间浪费。得不偿失。

       äºŽæ˜¯ï¼ŒçŽ°ä»£çš„编程语言的设计思路都是把同步锁加在代码段上。确切的说,是把同步锁加在“访问共享资源的代码段”上。这一点一定要记住,同步锁是加在代码段上的。

       åŒæ­¥é”åŠ åœ¨ä»£ç æ®µä¸Šï¼Œå°±å¾ˆå¥½åœ°è§£å†³äº†ä¸Šè¿°çš„空间浪费问题。但是却增加了模型的复杂度,也增加了我们的理解难度。

       çŽ°åœ¨æˆ‘们就来仔细分析“同步锁加在代码段上”的线程同步模型。

       é¦–先,我们已经解决了同步锁加在哪里的问题。我们已经确定,同步锁不是加在共享资源上,而是加在访问共享资源的代码段上。

       å…¶æ¬¡ï¼Œæˆ‘们要解决的问题是,我们应该在代码段上加什么样的锁。这个问题是重点中的重点。这是我们尤其要注意的问题:访问同一份共享资源的不同代码段,应该加上同一个同步锁;如果加的是不同的同步锁,那么根本就起不到同步的作用,没有任何意义。

       è¿™å°±æ˜¯è¯´ï¼ŒåŒæ­¥é”æœ¬èº«ä¹Ÿä¸€å®šæ˜¯å¤šä¸ªçº¿ç¨‹ä¹‹é—´çš„共享对象。

       Java语言的synchronized关键字

       ä¸ºäº†åŠ æ·±ç†è§£ï¼Œä¸¾å‡ ä¸ªä»£ç æ®µåŒæ­¥çš„例子。

       ä¸åŒè¯­è¨€çš„同步锁模型都是一样的。只是表达方式有些不同。这里我们以当前最流行的Java语言为例。Java语言里面用synchronized关键字给代码段加锁。整个语法形式表现为

       synchronized(同步锁) {

       // 访问共享资源,需要同步的代码段

       }

       è¿™é‡Œå°¤å…¶è¦æ³¨æ„çš„就是,同步锁本身一定要是共享的对象。

       â€¦ f1() {

       Object lock1 = new Object(); // 产生一个同步锁

       synchronized(lock1){

       // 代码段 A

       // 访问共享资源 resource1

       // 需要同步

       }

       }

       ä¸Šé¢è¿™æ®µä»£ç æ²¡æœ‰ä»»ä½•æ„ä¹‰ã€‚因为那个同步锁是在函数体内部产生的。每个线程调用这段代码的时候,都会产生一个新的同步锁。那么多个线程之间,使用的是不同的同步锁。根本达不到同步的目的。

       åŒæ­¥ä»£ç ä¸€å®šè¦å†™æˆå¦‚下的形式,才有意义。

       public static final Object lock1 = new Object();

       â€¦ f1() {

       synchronized(lock1){ // lock1 是公用同步锁

       // 代码段 A

       // 访问共享资源 resource1

       // 需要同步

       }

       ä½ ä¸ä¸€å®šè¦æŠŠåŒæ­¥é”å£°æ˜Žä¸ºstatic或者public,但是你一定要保证相关的同步代码之间,一定要使用同一个同步锁。

       è®²åˆ°è¿™é‡Œï¼Œä½ ä¸€å®šä¼šå¥½å¥‡ï¼Œè¿™ä¸ªåŒæ­¥é”åˆ°åº•æ˜¯ä¸ªä»€ä¹ˆä¸œè¥¿ã€‚为什么随便声明一个Object对象,就可以作为同步锁?

       åœ¨Java里面,同步锁的概念就是这样的。任何一个Object Reference都可以作为同步锁。我们可以把Object Reference理解为对象在内存分配系统中的内存地址。因此,要保证同步代码段之间使用的是同一个同步锁,我们就要保证这些同步代码段的synchronized关键字使用的是同一个Object Reference,同一个内存地址。这也是为什么我在前面的代码中声明lock1的时候,使用了final关键字,这就是为了保证lock1的Object Reference在整个系统运行过程中都保持不变。

       ä¸€äº›æ±‚知欲强的读者可能想要继续深入了解synchronzied(同步锁)的实际运行机制。Java虚拟机规范中(你可以在google用“JVM Spec”等关键字进行搜索),有对synchronized关键字的详细解释。synchronized会编译成 monitor enter, … monitor exit之类的指令对。Monitor就是实际上的同步锁。每一个Object Reference在概念上都对应一个monitor。

       è¿™äº›å®žçŽ°ç»†èŠ‚问题,并不是理解同步锁模型的关键。我们继续看几个例子,加深对同步锁模型的理解。

       public static final Object lock1 = new Object();

       â€¦ f1() {

       synchronized(lock1){ // lock1 是公用同步锁

       // 代码段 A

       // 访问共享资源 resource1

       // 需要同步

       }

       }

       â€¦ f2() {

       synchronized(lock1){ // lock1 是公用同步锁

       // 代码段 B

       // 访问共享资源 resource1

       // 需要同步

       }

       }

       ä¸Šè¿°çš„代码中,代码段A和代码段B就是同步的。因为它们使用的是同一个同步锁lock1。

       å¦‚果有个线程同时执行代码段A,同时还有个线程同时执行代码段B,那么这个线程之间都是要进行同步的。

       è¿™ä¸ªçº¿ç¨‹éƒ½è¦ç«žäº‰ä¸€ä¸ªåŒæ­¥é”lock1。同一时刻,只有一个线程能够获得lock1的所有权,只有一个线程可以执行代码段A或者代码段B。其他竞争失败的线程只能暂停运行,进入到该同步锁的就绪(Ready)队列。

       æ¯ä¸€ä¸ªåŒæ­¥é”ä¸‹é¢éƒ½æŒ‚了几个线程队列,包括就绪(Ready)队列,待召(Waiting)队列等。比如,lock1对应的就绪队列就可以叫做lock1 - ready queue。每个队列里面都可能有多个暂停运行的线程。

       æ³¨æ„ï¼Œç«žäº‰åŒæ­¥é”å¤±è´¥çš„线程进入的是该同步锁的就绪(Ready)队列,而不是后面要讲述的待召队列(Waiting Queue,也可以翻译为等待队列)。就绪队列里面的线程总是时刻准备着竞争同步锁,时刻准备着运行。而待召队列里面的线程则只能一直等待,直到等到某个信号的通知之后,才能够转移到就绪队列中,准备运行。

       æˆåŠŸèŽ·å–同步锁的线程,执行完同步代码段之后,会释放同步锁。该同步锁的就绪队列中的其他线程就继续下一轮同步锁的竞争。成功者就可以继续运行,失败者还是要乖乖地待在就绪队列中。

       å› æ­¤ï¼Œçº¿ç¨‹åŒæ­¥æ˜¯éžå¸¸è€—费资源的一种操作。我们要尽量控制线程同步的代码段范围。同步的代码段范围越小越好。我们用一个名词“同步粒度”来表示同步代码段的范围。

       åŒæ­¥ç²’度

       åœ¨Java语言里面,我们可以直接把synchronized关键字直接加在函数的定义上。

       æ¯”如。

       â€¦ synchronized … f1() {

       // f1 代码段

       }

       è¿™æ®µä»£ç å°±ç­‰ä»·äºŽ

       â€¦ f1() {

       synchronized(this){ // 同步锁就是对象本身

       // f1 代码段

       }

       }

       åŒæ ·çš„原则适用于静态(static)函数

       æ¯”如。

       â€¦ static synchronized … f1() {

       // f1 代码段

       }

       è¿™æ®µä»£ç å°±ç­‰ä»·äºŽ

       â€¦static … f1() {

       synchronized(Class.forName(…)){ // 同步锁是类定义本身

       // f1 代码段

       }

       }

       ä½†æ˜¯ï¼Œæˆ‘们要尽量避免这种直接把synchronized加在函数定义上的偷懒做法。因为我们要控制同步粒度。同步的代码段越小越好。synchronized控制的范围越小越好。

       æˆ‘们不仅要在缩小同步代码段的长度上下功夫,我们同时还要注意细分同步锁。

       æ¯”如,下面的代码

       public static final Object lock1 = new Object();

       â€¦ f1() {

       synchronized(lock1){ // lock1 是公用同步锁

       // 代码段 A

       // 访问共享资源 resource1

       // 需要同步

       }

       }

       â€¦ f2() {

       synchronized(lock1){ // lock1 是公用同步锁

       // 代码段 B

       // 访问共享资源 resource1

       // 需要同步

       }

       }

       â€¦ f3() {

       synchronized(lock1){ // lock1 是公用同步锁

       // 代码段 C

       // 访问共享资源 resource2

       // 需要同步

       }

       }

       â€¦ f4() {

       synchronized(lock1){ // lock1 是公用同步锁

       // 代码段 D

       // 访问共享资源 resource2

       // 需要同步

       }

       }

       ä¸Šè¿°çš„4段同步代码,使用同一个同步锁lock1。所有调用4段代码中任何一段代码的线程,都需要竞争同一个同步锁lock1。

       æˆ‘们仔细分析一下,发现这是没有必要的。

       å› ä¸ºf1()的代码段A和f2()的代码段B访问的共享资源是resource1,f3()的代码段C和f4()的代码段D访问的共享资源是resource2,它们没有必要都竞争同一个同步锁lock1。我们可以增加一个同步锁lock2。f3()和f4()的代码可以修改为:

       public static final Object lock2 = new Object();

       â€¦ f3() {

       synchronized(lock2){ // lock2 是公用同步锁

       // 代码段 C

       // 访问共享资源 resource2

       // 需要同步

       }

       }

       â€¦ f4() {

       synchronized(lock2){ // lock2 是公用同步锁

       // 代码段 D

       // 访问共享资源 resource2

       // 需要同步

       }

       }

       è¿™æ ·ï¼Œf1()和f2()就会竞争lock1,而f3()和f4()就会竞争lock2。这样,分开来分别竞争两个锁,就可以大大较少同步锁竞争的概率,从而减少系统的开销。

       ä¿¡å·é‡

       åŒæ­¥é”æ¨¡åž‹åªæ˜¯æœ€ç®€å•çš„同步模型。同一时刻,只有一个线程能够运行同步代码。

       æœ‰çš„时候,我们希望处理更加复杂的同步模型,比如生产者/消费者模型、读写同步模型等。这种情况下,同步锁模型就不够用了。我们需要一个新的模型。这就是我们要讲述的信号量模型。

       ä¿¡å·é‡æ¨¡åž‹çš„工作方式如下:线程在运行的过程中,可以主动停下来,等待某个信号量的通知;这时候,该线程就进入到该信号量的待召(Waiting)队列当中;等到通知之后,再继续运行。

       å¾ˆå¤šè¯­è¨€é‡Œé¢ï¼ŒåŒæ­¥é”éƒ½ç”±ä¸“门的对象表示,对象名通常叫Monitor。

       åŒæ ·ï¼Œåœ¨å¾ˆå¤šè¯­è¨€ä¸­ï¼Œä¿¡å·é‡é€šå¸¸ä¹Ÿæœ‰ä¸“门的对象名来表示,比如,Mutex,Semphore。

       ä¿¡å·é‡æ¨¡åž‹è¦æ¯”同步锁模型复杂许多。一些系统中,信号量甚至可以跨进程进行同步。另外一些信号量甚至还有计数功能,能够控制同时运行的线程数。

       æˆ‘们没有必要考虑那么复杂的模型。所有那些复杂的模型,都是最基本的模型衍生出来的。只要掌握了最基本的信号量模型——“等待/通知”模型,复杂模型也就迎刃而解了。

       æˆ‘们还是以Java语言为例。Java语言里面的同步锁和信号量概念都非常模糊,没有专门的对象名词来表示同步锁和信号量,只有两个同步锁相关的关键字——volatile和synchronized。

       è¿™ç§æ¨¡ç³Šè™½ç„¶å¯¼è‡´æ¦‚念不清,但同时也避免了Monitor、Mutex、Semphore等名词带来的种种误解。我们不必执着于名词之争,可以专注于理解实际的运行原理。

       åœ¨Java语言里面,任何一个Object Reference都可以作为同步锁。同样的道理,任何一个Object Reference也可以作为信号量。

       Object对象的wait()方法就是等待通知,Object对象的notify()方法就是发出通知。

       å…·ä½“调用方法为

       ï¼ˆ1)等待某个信号量的通知

       public static final Object signal = new Object();

       â€¦ f1() {

       synchronized(singal) { // 首先我们要获取这个信号量。这个信号量同时也是一个同步锁

       // 只有成功获取了signal这个信号量兼同步锁之后,我们才可能进入这段代码

       signal.wait(); // 这里要放弃信号量。本线程要进入signal信号量的待召(Waiting)队列

       // 可怜。辛辛苦苦争取到手的信号量,就这么被放弃了

       // 等到通知之后,从待召(Waiting)队列转到就绪(Ready)队列里面

       // 转到了就绪队列中,离CPU核心近了一步,就有机会继续执行下面的代码了。

       // 仍然需要把signal同步锁竞争到手,才能够真正继续执行下面的代码。命苦啊。

       â€¦

       }

       }

       éœ€è¦æ³¨æ„çš„是,上述代码中的signal.wait()的意思。signal.wait()很容易导致误解。signal.wait()的意思并不是说,signal开始wait,而是说,运行这段代码的当前线程开始wait这个signal对象,即进入signal对象的待召(Waiting)队列。

       ï¼ˆ2)发出某个信号量的通知

       â€¦ f2() {

       synchronized(singal) { // 首先,我们同样要获取这个信号量。同时也是一个同步锁。

       // 只有成功获取了signal这个信号量兼同步锁之后,我们才可能进入这段代码

       signal.notify(); // 这里,我们通知signal的待召队列中的某个线程。

       // 如果某个线程等到了这个通知,那个线程就会转到就绪队列中

       // 但是本线程仍然继续拥有signal这个同步锁,本线程仍然继续执行

       // 嘿嘿,虽然本线程好心通知其他线程,

       // 但是,本线程可没有那么高风亮节,放弃到手的同步锁

       // 本线程继续执行下面的代码

       â€¦

       }

       }

       éœ€è¦æ³¨æ„çš„是,signal.notify()的意思。signal.notify()并不是通知signal这个对象本身。而是通知正在等待signal信号量的其他线程。

       ä»¥ä¸Šå°±æ˜¯Object的wait()和notify()的基本用法。

       å®žé™…上,wait()还可以定义等待时间,当线程在某信号量的待召队列中,等到足够长的时间,就会等无可等,无需再等,自己就从待召队列转移到就绪队列中了。

       å¦å¤–,还有一个notifyAll()方法,表示通知待召队列里面的所有线程。

       è¿™äº›ç»†èŠ‚问题,并不对大局产生影响。

       ç»¿è‰²çº¿ç¨‹

       ç»¿è‰²çº¿ç¨‹ï¼ˆGreen Thread)是一个相对于操作系统线程(Native Thread)的概念。

       æ“ä½œç³»ç»Ÿçº¿ç¨‹ï¼ˆNative Thread)的意思就是,程序里面的线程会真正映射到操作系统的线程,线程的运行和调度都是由操作系统控制的

       ç»¿è‰²çº¿ç¨‹ï¼ˆGreen Thread)的意思是,程序里面的线程不会真正映射到操作系统的线程,而是由语言运行平台自身来调度。

       å½“前版本的Python语言的线程就可以映射到操作系统线程。当前版本的Ruby语言的线程就属于绿色线程,无法映射到操作系统的线程,因此Ruby语言的线程的运行速度比较慢。

       éš¾é“说,绿色线程要比操作系统线程要慢吗?当然不是这样。事实上,情况可能正好相反。Ruby是一个特殊的例子。线程调度器并不是很成熟。

       ç›®å‰ï¼Œçº¿ç¨‹çš„流行实现模型就是绿色线程。比如,stackless Python,就引入了更加轻量的绿色线程概念。在线程并发编程方面,无论是运行速度还是并发负载上,都优于Python。

       å¦ä¸€ä¸ªæ›´è‘—名的例子就是ErLang(爱立信公司开发的一种开源语言)。

       ErLang的绿色线程概念非常彻底。ErLang的线程不叫Thread,而是叫做Process。这很容易和进程混淆起来。这里要注意区分一下。

       ErLang Process之间根本就不需要同步。因为ErLang语言的所有变量都是final的,不允许变量的值发生任何变化。因此根本就不需要同步。

       final变量的另一个好处就是,对象之间不可能出现交叉引用,不可能构成一种环状的关联,对象之间的关联都是单向的,树状的。因此,内存垃圾回收的算法效率也非常高。这就让ErLang能够达到Soft Real Time(软实时)的效果。这对于一门支持内存垃圾回收的语言来说,可不是一件容易的事情。

Java多线程——singleThreadExecutor

       singleThreadExecutor,Java中Executors类的一个静态方法,创建了一个线程池,该线程池仅包含一个核心线程。这意味着所有任务将由这一单一线程执行,形成单线程执行模式。若核心线程因异常停止,则将启动新的线程替代,确保服务不中断。此线程池特别设计确保任务执行顺序与提交顺序一致,提升程序执行流程的可预测性与稳定性。

       创建singleThreadExecutor的代码示例如下:

       在这个例子中,ThreadPoolExecutor的corePoolSize和maximumPoolSize的值均为1,明确指出线程池仅包含一个核心线程,且最大线程数同样为1,保证了线程的高效利用。缓冲队列采用的是LinkedBlockingQueue,这是一个无边界队列,用于存储等待执行的任务。

【本文网址:http://hld.net.cn/news/30c535194618.html 欢迎转载】

copyright © 2016 powered by 皮皮网   sitemap