一文搞懂Java的多线程底层逻辑,再也不怕多线程了
Posted 香菜聊游戏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文搞懂Java的多线程底层逻辑,再也不怕多线程了相关的知识,希望对你有一定的参考价值。
目录
没什么想说的,就是想写两次Java的,顺便送两本书,好了,开始吧
1、线程是什么
线程是操作系统调度的最小单元,也叫轻量级进程。它被包含在进程之中,是进程中的实际运作单位。同一进程可以创建多个线程,每个进程都有自己独立的一块内存空间。并且能够访问共享的内存变量。
总之:线程就是一个电脑的工作单位,可以直接类比现实生活中的一个"劳动力"(一个人)
举个例子,开了一家餐厅,餐厅这个实体就是进程,餐厅里的服务员就是线程,餐厅里的座位就是资源(游戏内的数据),所有的服务员都可以安排客人就座,多个服务员安排座位就是多线程竞争,锁也就是去排号。线程池就是有多个服务员一直站在那里等着被呼叫。
进程和线程的区别可以通俗理解为进程是一个公司,而线程是公司里的工作人员,真正干活的还是个人
2、启动线程
java创建线程的三种方式:
-
*继承Thread类创建线程类*,无法继承其他类。
-
*实现Runnable接口*
-
*通过Callable和Future创建线程*
package thread; /** * @author 香菜 */ public class ExtendThread extends Thread { @Override public void run() { System.out.println("ExtendThread");; } } package thread; import java.util.concurrent.Callable; /** * @author 香菜 */ public class ImpCallable implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("Callable "); return 1; } } package thread; import java.util.concurrent.FutureTask; /** * @author 香菜 */ public class Aain { public static void main(String[] args) { new ExtendThread().start(); new Thread(new ImpRunnable()).start(); new Thread(new FutureTask<>(new ImpCallable())).start(); } }
总结:线程的概念来自于生活,理解了概念,在项目中思考的时候只要搞清楚项目的线程模型,基本上不会遇到太大的问题
3、线程池
线程池存在的原因是为了节省创建线程和销毁线程的系统损耗,这样说可能不太好理解,我们直接通俗点解释。
我们做了一个饭馆,大家都知道饭馆的营业时间是有周期性的,也就是饭点的时候客人才多,在其他的时间饭店里肯定是没有人的,我们怎么样雇人帮忙呐?
假如我们在看到客人多的时候感觉招募两个店员,然后开始让他们进行干活,饭点过了就开掉,这样的话就是开启一个线程,没事做了赶紧销毁掉。这样的处理逻辑明显不符合饭馆的操作,大家都知道每个饭馆的工作人员基本上都是固定的,为什么这样呐?首先开启线程,也就是招聘会有开销,比如发广告,面试,这些都很费时间,而且招到以后还要培训,如果用完之后直接开掉,肯定是不合适的,所以这个时候我们需要一些长期的工作人员维持在店里,也就是线程池了。
线程池的原理是同样的:保留一部分的线程在系统内,避免线程的创建和销毁的系统消耗,随取随用。
4、线程池的创建
java中创建线程池的方式一般有两种:
通过Executors工厂方法创建
通过new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)自定义创建
通过Executors工厂方法创建
Executor 提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。
相当于manager,老板让manager去执行一件任务,具体的是谁执行,什么时候执行,就不管了。
介绍几个
内置的线程池基本上都在这里
newScheduledThreadPool 定时执行的线程池
newCachedThreadPool 缓存使用过的线程
newFixedThreadPool 固定数量的线程池
newWorkStealingPool 将大任务分解为小任务的线程池
通过构造函数创建
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) ;
int corePoolSize :表示线程池的数量,常态下的线程数量
int maximumPoolSize :表示线程池最大的线程池数量,极限情况下的最大数量
long keepAliveTime :表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了
TimeUnit unit :参数keepAliveTime的时间单位,
BlockingQueue<Runnable> workQueue : 一个阻塞队列,用来存储等待执行的任务
常见选择有:
ArrayBlockingQueue 少用 LinkedBlockingQueue 常用 SynchronousQueue 常用 PriorityBlockingQueue 少用
ThreadFactory threadFactory :用于设置创建线程的工厂,可以设置线程的名字和优先级等等
RejectedExecutionHandler handler:看类名也能猜到是拒绝处理任务时的策略。主要有下面几种可以选择
1、AbortPolicy:直接抛出异常。 2、CallerRunsPolicy:只用调用者所在线程来运行任务。 3、DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。 4、DiscardPolicy:不处理,丢弃掉。 5、也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。
5、调试线程
多线程的调试可能是一些同学不太会,大概说下怎么回事
下面创建了一个10个线程的线程池,并提交了3个任务,运行的时候生成了三个线程创建了一个3个线程的线程池,提交了3个任务,运行的时候生成了三个线程
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(()-> System.out.println("1111111111111111111111"));
executorService.submit(()-> System.out.println("222222222222222222"));
executorService.submit(()-> System.out.println("3333333333333333333333"));
添加断点:
添加断点应该每个同学都会,就是在调试的时候需要注意下面两个选项
All 就是在断点发生的时候,会将整个虚拟机停住,也就是所有的线程都会暂停
Thread 就是断住当前线程,其他的线程不收影响,在调试多线程的时候一定要选择这个,测试多个线程的并行
6、synchronized关键字
每个java对象头中都有锁状态位标记。java中在使用synchronize同步的时候,肯定是涉及到某个对象的锁。因此,在考虑同步的时候,首先要想到是同步的是哪个对象的锁。
每个对象都和一个monitor对象关联,主要用来控制互斥资源的访问,如果你想要加锁必须先获得monitor的批准,如果现在正有线程访问,会把申请的线程加入到等待队列。在对临界资源的加锁的时候会调用monitor_enter,离开的时候会monitorexit 释放锁
1、 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对class对象的锁,该类所有的对象同一把锁。
2、每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
3、实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制,避免做嵌套synchronized 的使用。
4、synchronized 要尽量控制范围,不能范围太大,否则会损失系统性能。
最后常规送书:
本书共有15章,内容涉及Java线程堆栈分析、性能瓶颈分析、内存泄漏分析和堆内存设置、并发和多线程、幽灵代码、常见的Java陷阱、数据库、字符集与编码、JVM运行参数、常用问题定位工具、计算架构与存储架构、开发语言等的选择、设计软件系统、工程实践、常见案例等内容。
京东自营购买链接:
https://item.jd.com/12711001.html
当当自营购买链接:
http://product.dangdang.com/29115495.html
大家点赞关注,三天后在留言的同学中抽取送两本书
注:如果中奖了没关注则放弃
以上是关于一文搞懂Java的多线程底层逻辑,再也不怕多线程了的主要内容,如果未能解决你的问题,请参考以下文章