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

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

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

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

1.png

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

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去执行一件任务,具体的是谁执行,什么时候执行,就不管了。

 介绍几个

2.png

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

newScheduledThreadPool 定时执行的线程池
 
newCachedThreadPool 缓存使用过的线程
 
newFixedThreadPool 固定数量的线程池
 
newWorkStealingPool 将大任务分解为小任务的线程池

3.png

通过构造函数创建
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"));

添加断点:

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

4.png

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

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

5.png

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

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

6.jpg

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

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

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

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

免责声明:务必仔细阅读

  • 本站为个人博客,博客所转载的一切破解、path、补丁、注册机和注册信息及软件等资源文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。

  • 本站为非盈利性站点,打赏作为用户喜欢本站捐赠打赏功能,本站不贩卖软件等资源,所有内容不作为商业行为。

  • 本博客的文章中涉及的任何解锁和解密分析脚本,仅用于测试和学习研究,禁止用于商业用途,不能保证其合法性,准确性,完整性和有效性,请根据情况自行判断.

  • 本博客的任何内容,未经许可禁止任何公众号、自媒体进行任何形式的转载、发布。

  • 博客对任何脚本资源教程问题概不负责,包括但不限于由任何脚本资源教程错误导致的任何损失或损害.

  • 间接使用相关资源或者参照文章的任何用户,包括但不限于建立VPS或在某些行为违反国家/地区法律或相关法规的情况下进行传播, 博客对于由此引起的任何隐私泄漏或其他后果概不负责.

  • 请勿将博客的任何内容用于商业或非法目的,否则后果自负.

  • 如果任何单位或个人认为该博客的任何内容可能涉嫌侵犯其权利,则应及时通知并提供身份证明,所有权证明至admin@proyy.com.我们将在收到认证文件后删除相关内容.

  • 任何以任何方式查看此博客的任何内容的人或直接或间接使用该博客的任何内容的使用者都应仔细阅读此声明。博客保留随时更改或补充此免责声明的权利。一旦使用并复制了博客的任何内容,则视为您已接受此免责声明.

您必须在下载后的24小时内从计算机或手机中完全删除以上内容.

您使用或者复制了本博客的任何内容,则视为已接受此声明,请仔细阅读


更多福利请关注一一网络微信公众号或者小程序

一一网络微信公众号
打个小广告,宝塔服务器面板,我用的也是,很方便,重点是免费的也能用,没钱太难了,穷鬼一个,一键全能部署及管理,送你3188元礼包,点我领取https://www.bt.cn/?invite_code=MV9kY3ZwbXo=


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

发表评论

发表评论

一一网络-提供最优质的文章集合

立即查看 了解详情