您的位置 首页 java

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

1、 线程 是什么

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

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

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

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

2、启动线程

java 创建线程的三种方式:

 
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 ThreadPool executor (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)自定义创建

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

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

介绍几个

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

newScheduledThreadPool 定时执行的线程池

newCachedThreadPool 缓存使用过的线程

newFixedThreadPool 固定数量的线程池

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

通过构造函数创建 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue, Thread Factory threadFactory,RejectedExecutionHandler handler) ;

int corePoolSize :表示线程池的数量,常态下的线程数量

int maximumPoolSize :表示线程池最大的线程池数量,极限情况下的最大数量

long keepAliveTime :表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了

TimeUnit unit :参数keepAliveTime的时间单位,

B lock ingQueue 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”));

添加断点:

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

一文搞懂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 要尽量控制范围,不能范围太大,否则会损失系统性能。

作者:香菜聊游戏
链接:

文章来源:智云一二三科技

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

文章地址:https://www.zhihuclub.com/198491.shtml

关于作者: 智云科技

热门文章

网站地图