Java多线程详解【面试+工作】 Java线程:新特征-信号量 Java的信号量实际上是一个功能完毕的计数器,对控制一定资源的消费与回收有着很重要的意义,信号量常常用于多线程的代码中,并能监控有多少数目的线程等待获取资源 Java线程:新特征-阻塞队列 阻塞队列是Java5线程新特征中的内容,Java定义了阻塞队列的接口java.util.concurrent.BlockingQueue,阻塞队列的概念是,一个指定长度的队列 Java线程:新特征-条件变量 条件变量是Java5线程中很重要的一个概念,顾名思义,条件变量就是表示条件的一种变量。 条件变量的出现是为了更精细控制线程等待与唤醒,在Java5之前,线程的等待与唤醒依靠的是Object对象的wait()和notify()/notifyAll()方法,这样的处理不够精细。 而在Java5中,一个锁可以有多个条件,每个条件上可以有多个线程等待,通过调用await()方法,可以让线程在该条件下等待。当调用signalAll()方法,又可以唤醒该条件下的等待的线程。
服务器端为了能流畅处理多个客户端链接,一般在某个线程A里面accept新的客户端连接并生成新连接的socket fd,然后将这些新连接的socketfd给另外开的数个工作线程B1、B2、B3、B4,这些工作线程处理这些新连接上的网络 这里我们将线程A称为主线程,B1、B2、B3、B4等称为工作线程。工作线程的代码框架一般如下: while (! 线程A接收的新连接,可以根据一定的负载均衡原则将新的socket fd分配给工作线程。 如此反复,也就是说线程A记录了各个工作线程上的socket fd数量,这样可以最大化地来平衡资源,避免一些工作线程“忙死”,另外一些工作线程“闲死”的现象。 3. 即使工作线程不满载的情况下,也可以让工作线程做其他的事情。比如现在有四个工作线程,但只有三个连接。那么线程B4就可以在handle_other_thing()做一些其他事情。
用户线程 AfxBeginThread 创建线程 AfxEndThread 结束线程 添加类->父类是CWndThread 在CWndThread::InitInstance()函数中实现窗口 1)定义类 1.定义窗口类对象 2.将窗口对象设置为主窗口 3.显示窗口 CMyDialog Dlg; m_pMainWnd = &Dlg; Dlg.DoModal(); 2)创建用户线程 CuserThread * pThread = (CuserThread*)AfxBeginThread(RUNTIME_CLASS(CuserThread));//创建线程 工作线程 AfxBeginThread 创建线程 AfxEndThread 结束线程 1.定义线程处理函数 函数格式 static UINT Thread(LPVOID pParam);//需要是静态成员函数 2.创建线程 AfxBeginThread (XXX,this); 在线程内使用成员变量的方法:(将pParam强转成类地址) XXXClasName* pThis = (XXXClasName*)pParam;
AWT 和 Swing 就使用这个模型,在这个模型中有一个 GUI 事件线程,导致用户界面发生变化的所有工作都必须在该线程中执行。 因此,Swing 应用程序经常需要额外的工作线程,用于运行时间很长的、同 UI 有关的任务。 每个任务对应一个线程方法和单个后台线程(single-background-thread)方法在某些情形下都工作得非常理想。每个任务一个线程方法在只有少量运行时间很长的任务时工作得十分好。 而只要调度可预见性不是很重要,则单个后台线程方法就工作得十分好,如低优先级后台任务就是这种情况。 我们通常想要的是同一组固定的工作线程相结合的工作队列,它使用 wait() 和 notify() 来通知等待线程新的工作已经到达了。该工作队列通常被实现成具有相关监视器对象的某种链表。
定义 让有限的工作线程(Worker Thread)来轮流异步处理无限多的任务。也可以将其归类为分工模式,它的典型实现 就是线程池,也体现了经典设计模式中的享元模式。 例如,海底捞的服务员(线程),轮流处理每位客人的点餐(任务),如果为每位客人都配一名专属的服务员,那 么成本就太高了(对比另一种多线程设计模式:Thread-Per-Message) 注意,不同任务类型应该使用不同的线程池 饥饿 固定大小线程池会有饥饿现象 两个工人是同一个线程池中的两个线程 他们要做的事情是:为客人点餐和到后厨做菜,这是两个阶段的工作 客人点餐:必须先点完餐,等菜做好,上菜,在此期间处理点餐的工人必须等待 上菜宫保鸡丁2 创建多少线程池合适 线程池的大小应根据具体的应用场景和系统需求来确定。以下是一些建议供参考: 考虑系统资源:线程池的大小应该与系统可用的资源相匹配。 考虑任务类型:不同类型的任务对线程池的需求量不同。
序 本文主要来展示一下简版的work stealing线程池的实现。
大家好,又见面了,我是全栈君 每个系统都有线程,而线程的最重要的作用就是并行处理,提高软件的并发率。针对界面来说,还能提高界面的响应力。 线程分为界面线程和工作者线程,界面实际就是一个线程画出来的东西,这个线程维护一个“消息队列”,“消息队列”也是界面线程和工作者线程的最大区别,这个词应该进到你的脑子里,根深蒂固的! ,我们就要注意线程的同步问题了,线程的同步一般来说,是在多个线程共用了资源的时候。 上面已经说了线程的创建、管理(退出线程、等待线程)、同步等,那我们发现了什么共性呢?作为一个程序员,我们要很敏感的发现这些代码上的共性,这是我们设计代码的主要前提。 // 启动线程,线程所需要的参数从这里传进 BOOL End(); // 结束线程 virtual void Run(); // 重写Run函数 hovertree.com 所以整个的线程封装成以下的类
最近在看《Java并发编程的艺术》回顾线程池的原理和参数的时候发现一个问题,如果 corePoolSize = 0 且 阻塞队列是无界的。线程池将如何工作? 我们先回顾一下书里面描述线程池execute()工作的逻辑: 如果当前运行的线程,少于corePoolSize,则创建一个新的线程来执行任务。 { return c & CAPACITY; } // 根据工作线程数和线程池状态获取 ctl private static int ctlOf(int rs, int wc) 如果线程池处于 Running状态,则检查工作线程(worker)是否为0。如果为0,则创建新的线程来处理任务。如果启动线程数大于maximumPoolSize,任务将被拒绝策略拒绝。 线程池将如何工作? 这个问题应该就不难回答了。 最后 《Java并发编程的艺术》是一本学习 java 并发编程的好书,在这里推荐给大家。
1、线程状态和工作线程数量 首先线程池是有状态的,不同状态下线程池的行为是不一样的,5种状态已经在上面说过了。 ctl 的高3位用来表示线程池的状态(runState),低29位用来表示工作线程的个数(workerCnt),为什么要用3位来表示线程池的状态呢,原因是线程池一共有5种状态,而2位只能表示出4种情况, 所以至少需要3位才能表示得了5种状态。 5、非核心线程存活时间 上面我们说了当工作线程数达到 corePoolSize 时,线程池会将新接收到的任务存放在阻塞队列中,而阻塞队列又两种情况:一种是有界的队列,一种是无界的队列。 还是跟工作线程的个数有关,每一个线程在取任务的时候,线程池会比较当前的工作线程个数与核心线程数: 如果工作线程数小于当前的核心线程数,则使用第一种方法取任务,也就是没有超时回收,这时所有的工作线程都是“
线程池的概念 本质就是一个能够容纳多个线程的容器,其中的线程可以反复利用,省去了频繁创建线程对象的操作,无需因为反复创建线程对象而消耗过多资源 工作线程(PoolWorker) 表示线程池中的线程, 在没有任务时处于等待状态,可以循环的执行任务 任务队列(TaskQueue) 用于存放没有处理的任务,提供一种缓冲机制 任务接口(Task) 每个任务必须实现的接口,一共工作线程调度任务的执行,它主要规定了任务的入口 ,任务执行完后的收尾工作,任务的执行状态等等 线程池管理器(ThreadPool) 用于创建并管理线程。 通俗的讲就是任务来后就分配一个线程使用,线程处于占用状态,如果任务执行完毕,线程归还于线程池,并且暂处于空闲状态 合理利用线程池的好处 降低资源消耗 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用 Runnable{ @Override public void run() { for(int i=0;i<5;i++){ System.out.println
NodeJs为异步单线程模型,其中如果主线程如果执行比较耗CPU的操作,可能会失去响应,因此工作线程(Worker)对于执行 CPU 密集型 JavaScript 操作非常有用。 worker_threads模块允许使用并行执行 JavaScript 的线程。模块包含跟worker线程通信的接口。 isMainThread) {//在worker线程 //获取host传来的参数 const data = workerData; //进行一些高CPU的操作 //如:排序
除了路径解析不同,创建子工作者线程与创建普通工作者线程是一样的。子工作者线程的脚本路径根据父工作者线程而不是相对于网页来解析。 factorialWorker.js');factorialWorker.onmessage = ({data}) => console.log(data);factorialWorker.postMessage(5) ;factorialWorker.postMessage(7);factorialWorker.postMessage(10);// 5! (5);// 5! 通过使用特定于任务的线程池,可以分配固定数量的工作者线程,并根据需要为他们提供参数。工作者线程会接收这些参数,执行耗时的计算,并把结果返回给线程池。然后线程池可以再将其他工作分派给工作者线程去执行。
TimeUnit.SECONDS, //unit new LinkedBlockingDeque<>(100));//workQueue for (int i = 0; i < 5; 这边会用“工作者”来代替线程的说法,如果在java中这个工作者就是线程。 工作窃取核心思想是,自己的活干完了去看看别人有没有没干完的活,如果有就拿过来帮他干。 Java中的工作窃取算法线程池 在Java 1.7新增了一个ForkJoinPool类,主要是实现了工作窃取算法的线程池,该类在1.8中被优化了,同时1.8在Executors类中还新增了两个newWorkStealingPool 如果你能知道输出什么那么你对这个机制就算掌握了,会输出当前运行环境中处理器(cpu)数量的次数(如果核算大于5就只会输出5个结果)。 ForkJoinPool中的工作队列,工作窃取都是需要额外管理的,同时也对线程调度和GC带来了压力。 所以ForkJoinPool并不是万能药大家根据具体需要去使用。
TimeUnit.SECONDS, //unit new LinkedBlockingDeque<>(100));//workQueue for (int i = 0; i < 5; 这边会用“工作者”来代替线程的说法,如果在java中这个工作者就是线程。 工作窃取核心思想是,自己的活干完了去看看别人有没有没干完的活,如果有就拿过来帮他干。 Java中的工作窃取算法线程池 在Java 1.7新增了一个ForkJoinPool类,主要是实现了工作窃取算法的线程池,该类在1.8中被优化了,同时1.8在Executors类中还新增了两个newWorkStealingPool 如果你能知道输出什么那么你对这个机制就算掌握了,会输出当前运行环境中处理器(cpu)数量的次数(如果核算大于5就只会输出5个结果)。 ForkJoinPool中的工作队列,工作窃取都是需要额外管理的,同时也对线程调度和GC带来了压力。所以ForkJoinPool并不是万能药大家根据具体需要去使用。
前言 在electron中如何使用nodejs的工作线程,Demo代码 代码 工作线程 // workerThreads.js const { Worker, isMainThread, parentPort
工作线程(耗时操作)与UI线程实现异步更新 概述:工作线程A(多任务)执行期间,把单一任务的结果返回到UI线程更新。 实现: 创建基础流程类(两个handler) public abstract class BaseQueueThread { /** * 设置处理线程、ui处理流程,由子类实现 (); msg.what = priority; msg.obj = obj; mWorkHandler.sendMessage(msg);//从工作线程开始 protected void doInUi(Object obj) { mTextView.setText((String)obj); } } 发送工作线程消息 ,关键点要处理好什么时候执行工作线程,什么时候执行UI线程。
一、什么是线程池: 线程池主要是为了解决 新任务执行时,应用程序为任务创建一个新线程 以及 任务执行完毕时,销毁线程所带来的开销。 通过线程池,可以在项目初始化时就创建一个线程集合,然后在需要执行新任务时重用这些线程而不是每次都新建一个线程,一旦任务已经完成了,线程回到线程池中并等待下一次分配任务,达到资源复用的效果。 初始化时线程数量为零,之后每次提交一个任务就创建一个线程,直到线程达到线程池的最大容量。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。 (4)unit:keepAliveTime空闲线程存活时间的单位; (5)workQueue:阻塞队列,用来存放等待被执行的任务,且任务必须实现Runnable接口,在JDK中提供了如下阻塞队列: ArrayBlockingQueue ThreadPoolExecutor.DiscardPolicy:将直接丢弃此线程。 (5)当一个线程完成任务时,它会从workQueue中获取下一个任务来执行。
在这里还得提一下我们在 Java 多线程(3)— 线程的同步(上) 中提到的 Java 规定的用来完成线程工作内存和主内存数据交互的 8 种原子性的操作。 操作使用 load 操作:其把 read 操作从主内存中得到的变量值复制到当前线程工作内存的变量副本中 store 操作:作用于线程工作内存的变量,其把工作内存中的变量的值传输到主内存中,供接下来的 用 volatile 关键字修饰的变量,在线程的工作内存中使用之前一定会通过 load 操作来将变量的值从主内存读取到线程的工作内存中,而在修改完成之后一定会通过 store 操作来将修改后变量的值刷新到主内存中 其实对于单例模式,在 Java 5 之后用 enum 关键字(枚举)实现是一个比较好的写法,(当然还有其他的实现方法)。这里只针对 DCL 来进行讨论。 原子类 其实,为了方便我们编写多线程程序,在 Java SE5 中已经引入了 AtomicInteger 、AtomicLong、AtomicReference 等特殊的原子类来保证我们在使用这些类时可以不主动加入额外的同步手段来保证程序的正确性
线程本地变量 在集成 Thread 类或实现 Runnable 接口时,不同线程中的数据共享是必要的。 【示例】 创建一个线程并实现 Runnable 接口。 并设置一个 Date 对象 并在开始和结束时打印线程开始执行的时间 public class UnsafeTask implements Runnable{ private Date startDate ,由于开始时间是线程间的共享变量,最终导致线程结束时答应的时间被修改。 线程本地变量 上述案例可视为 ? 多个线程共享同一变量,线程对这个变量同时进行修改 那么此时我们就需要引入线程本地变量,线程本地变量可以用如下图进行表示 ? 即线程本地变量只能为当前线程所拥有,不会与其他线程共享。
在深度工作的实践中我们会遇到很多问题,如何具体实践作者推荐了以下4个原则。 原则一、关注点放到极端重要的事情上。 正如《高效能人士的执行4原则》的作者所说的:“你想做的事情越多,完成的事情反而越少。 比如后续工作需要用到的技能、锻炼身体等 少做紧急不重要的事,因为紧急很容易把时间花费在这些任务上。比如某些不需要参加的会议,某个不重要的电话。 不做不重要也不紧急的事。 如果我们的目的是锻炼深度工作能力,那引领性指标就是”专注于极度重要目标上的深度工作状态时间“ 原则3:准备一个醒目的计分板 在团队内公开每个人的引领性指标,这个计分板可以制造一种竞争氛围,驱使他们专注于这些指标 复盘的目的就是分析行动所带来的反馈,以便更好的工作。 每次深度工作结束之后,及时对比计划与实施之间的差距,以便更好的认知自己目前的能力,通过调整计划来认识自己,养成习惯。 我们经常对自己在固定时间内能完成多少工作不甚清晰,通过深度工作来认识到自己的产出能力,及时调整计划,才能逐步的提升。