一文搞懂Java的多线程底层逻辑,再也不怕多线程了

一文搞懂Java的多线程底层逻辑,再也不怕多线程了1、线程是什么线程是操作系统调度的最小单元,也叫轻量级进程。它被包含在进程之中,是进程中的实际运作单位。同一进程可以创建多个线程,每个进程都有自

欢迎大家来到IT世界,在知识的湖畔探索吧!

1、线程是什么

线程是操作系统调度的最小单元,也叫轻量级进程。它被包含在进程之中,是进程中的实际运作单位。同一进程可以创建多个线程,每个进程都有自己独立的一块内存空间。并且能够访问共享的内存变量。

总之:线程就是一个电脑的工作单位,可以直接类比现实生活中的一个”劳动力”(一个人)

举个例子,开了一家餐厅,餐厅这个实体就是进程,餐厅里的服务员就是线程,餐厅里的座位就是资源(游戏内的数据),所有的服务员都可以安排客人就座,多个服务员安排座位就是多线程竞争,锁也就是去排号。线程池就是有多个服务员一直站在那里等着被呼叫。

一文搞懂Java的多线程底层逻辑,再也不怕多线程了

进程和线程的区别可以通俗理解为进程是一个公司,而线程是公司里的工作人员,真正干活的还是个人

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();
  }
}
复制代码

欢迎大家来到IT世界,在知识的湖畔探索吧!

总结:线程的概念来自于生活,理解了概念,在项目中思考的时候只要搞清楚项目的线程模型,基本上不会遇到太大的问题

3、线程池

线程池存在的原因是为了节省创建线程和销毁线程的系统损耗,这样说可能不太好理解,我们直接通俗点解释。

我们做了一个饭馆,大家都知道饭馆的营业时间是有周期性的,也就是饭点的时候客人才多,在其他的时间饭店里肯定是没有人的,我们怎么样雇人帮忙呐?

假如我们在看到客人多的时候感觉招募两个店员,然后开始让他们进行干活,饭点过了就开掉,这样的话就是开启一个线程,没事做了赶紧销毁掉。这样的处理逻辑明显不符合饭馆的操作,大家都知道每个饭馆的工作人员基本上都是固定的,为什么这样呐?首先开启线程,也就是招聘会有开销,比如发广告,面试,这些都很费时间,而且招到以后还要培训,如果用完之后直接开掉,肯定是不合适的,所以这个时候我们需要一些长期的工作人员维持在店里,也就是线程池了。

线程池的原理是同样的:保留一部分的线程在系统内,避免线程的创建和销毁的系统消耗,随取随用。

4、线程池的创建

java中创建线程池的方式一般有两种:

通过Executors工厂方法创建

通过new ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)自定义创建

通过Executors工厂方法创建 Executor 提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。

相当于manager,老板让manager去执行一件任务,具体的是谁执行,什么时候执行,就不管了。

介绍几个

一文搞懂Java的多线程底层逻辑,再也不怕多线程了

内置的线程池基本上都在这里

newScheduledThreadPool 定时执行的线程池

newCachedThreadPool 缓存使用过的线程

newFixedThreadPool 固定数量的线程池

newWorkStealingPool 将大任务分解为小任务的线程池

一文搞懂Java的多线程底层逻辑,再也不怕多线程了

通过构造函数创建 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 workQueue : 一个阻塞队列,用来存储等待执行的任务

常见选择有:

ArrayBlockingQueue 少用

LinkedBlockingQueue 常用

SynchronousQueue 常用

PriorityBlockingQueue 少用

ThreadFactory threadFactory :用于设置创建线程的工厂,可以设置线程的名字和优先级等等

RejectedExecutionHandler handler:看类名也能猜到是拒绝处理任务时的策略。主要有下面几种可以选择

欢迎大家来到IT世界,在知识的湖畔探索吧!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”));

添加断点:

添加断点应该每个同学都会,就是在调试的时候需要注意下面两个选项

一文搞懂Java的多线程底层逻辑,再也不怕多线程了

All 就是在断点发生的时候,会将整个虚拟机停住,也就是所有的线程都会暂停

Thread 就是断住当前线程,其他的线程不收影响,在调试多线程的时候一定要选择这个,测试多个线程的并行

一文搞懂Java的多线程底层逻辑,再也不怕多线程了

6、synchronized关键字

每个java对象头中都有锁状态位标记。java中在使用synchronize同步的时候,肯定是涉及到某个对象的锁。因此,在考虑同步的时候,首先要想到是同步的是哪个对象的锁。

每个对象都和一个monitor对象关联,主要用来控制互斥资源的访问,如果你想要加锁必须先获得monitor的批准,如果现在正有线程访问,会把申请的线程加入到等待队列。在对临界资源的加锁的时候会调用monitor_enter,离开的时候会monitorexit 释放锁

一文搞懂Java的多线程底层逻辑,再也不怕多线程了

1、 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对class对象的锁,该类所有的对象同一把锁。

2、每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。

3、实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制,避免做嵌套synchronized 的使用。

4、synchronized 要尽量控制范围,不能范围太大,否则会损失系统性能。

作者:香菜聊游戏
链接:https://juejin.cn/post/7065162333877714951

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/36810.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信