当前位置: 首页 / 技术分享 / 正文
好程序员Java培训分享Java多线程并发

2020-07-14

Java培训 好程序员

  好程序员Java培训分享Java多线程并发,1 Java 线程实现/创建方式

  继承 Thread

  Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过 Thread 类的 start()实例方法。start()方法是一个 native 方法,它将启动一个新线程,并执行 run()方法。

QQ截图20190505135426

 

public class MyThread extends Thread {

 

public void run() {

 

System.out.println("MyThread.run()");

 

} }

 

MyThread myThread1 = new MyThread();

 

myThread1.start();

 

实现 Runnable 接口

 

如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个Runnable 接口。

 

public class MyThread extends OtherClass implements Runnable {

 

public void run() {

 

System.out.println("MyThread.run()");

 

} } //启动

 

MyThreadMyThread myThread = new MyThread();

 

Thread thread = new Thread(myThread);

 

thread.start();

 

target.run()public void run() {

 

if (target != null) {

 

target.run();

 

} }

 

ExecutorServiceCallableFuture 有返回值线程

 

有返回值的任务必须实现 Callable 接口,类似的,无返回值的任务必须 Runnable 接口。执行Callable 任务后,可以获取一个 Future 的对象,在该对象上调用 get 就可以获取到 Callable 任务返回的 Object 了,再结合线程池接口 ExecutorService 就可以实现传说中有返回结果的多线程了。

 

//创建一个线程池

 

ExecutorService pool = Executors.newFixedThreadPool(taskSize);

 

// 创建多个有返回值的任务

 

List<Future> list = new ArrayList<Future>();

 

for (int i = 0; i < taskSize; i++) {

 

Callable c = new MyCallable(i + " ");

 

// 执行任务并获取 Future 对象

 

Future f = pool.submit(c); list.add(f);

 

} // 关闭线程池pool.shutdown();

 

// 获取所有并发任务的运行结果for (Future f : list) {

 

// Future 对象上获取任务的返回值,并输出到控制台System.out.println("res" + f.get().toString());

 

}

 

基于线程池的方式

 

线程和数据库连接这些资源都是非常宝贵的资源。如果每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池。

 

// 创建线程池ExecutorService threadPool = Executors.newFixedThreadPool(10);

 

while(true) { threadPool.execute(new Runnable() {

 

// 提交多个线程执行 @Override public void run() {

 

System.out.println(Thread.currentThread().getName() + " is running ..");

 

try {

 

Thread.sleep(3000);

 

}

 

catch (InterruptedException e) {

 

e.printStackTrace();

 

} } }); }}

 

2 同步锁与死锁

 

同步锁当多个线程同时访问同一个数据时,很容易出现问题。为了避免这种情况出现,我们要保证线程同步互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。Java中可以使用synchronized关键字来取得一个对象的同步锁。

 

死锁何为死锁,就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。

 

3 线程池原理

 

线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。主要特点为:线程复用;控制最大并发数;管理线程。

 

线程复用一个Thread的类都有一个start方法。当调用start启动线程时Java虚拟机会调用该类的 run 方法。那么该类的 run() 方法中就是调用了 Runnable 对象的 run() 方法。我们可以继承重写 Thread 类,在其 start 方法中添加不断循环调用传递过来的 Runnable 对象。这就是线程池的实现原理。循环方法中不断获取 Runnable 是用 Queue 实现的,在获取下一个 Runnable 之前可以是阻塞的。

 

线程池的组成一般的线程池主要分为以下 4 个组成部分:

 

(1)线程池管理器:用于创建并管理线程池。(2)工作线程:线程池中的线程。(3)任务接口:每个任务必须实现的接口,用于工作线程调度其运行。(4)任务队列:用于存放待处理的任务,提供一种缓冲机制。

 

Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 ExecutorExecutorsExecutorServiceThreadPoolExecutor Callable FutureFutureTask 这几个类。

 

ThreadPoolExecutor 的构造方法如下:

 

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);}

 

corePoolSize:指定了线程池中的线程数量。

 

maximumPoolSize:指定了线程池中的最大线程数量。

 

keepAliveTime:当前线程池数量超过 corePoolSize 时,多余的空闲线程的存活时间,即多次时间内会被销毁。

 

unitkeepAliveTime 的单位。

 

workQueue:任务队列,被提交但尚未被执行的任务。

 

threadFactory:线程工厂,用于创建线程,一般用默认的即可。

 

handler:拒绝策略,当任务太多来不及处理,如何拒绝任务。

 

拒绝策略线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了,再也塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这个问题。

 

JDK 内置的拒绝策略如下:

 

AbortPolicy :直接抛出异常,阻止系统正常运行。

 

CallerRunsPolicy :只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。

 

DiscardOldestPolicy :丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。

 

DiscardPolicy :该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这是zuihao的一种方案。

 

以上内置拒绝策略均实现了 RejectedExecutionHandler 接口,若以上策略仍无法满足实际需要,完全可以自己扩展 RejectedExecutionHandler 接口。

 

Java 线程池工作过程(1)线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。

 

(2)当调用 execute() 方法添加一个任务时,线程池会做如下判断:

 

a) 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;

 

b) 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;

 

c) 如果这时候队列满了,而且正在运行的线程数量小maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

 

d) 如果队列满了,而且正在运行的线程数量大于或等maximumPoolSize,那么线程池会抛出异常 RejectExecutionException

 

(3)当一个线程完成任务时,它会从队列中取下一个任务来执行。

 

(4)当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

好程序员公众号

  • · 剖析行业发展趋势
  • · 分享大厂面试心得
  • · 汇聚企业项目源码
  • · 下载全套高精尖教程

好程序员开班动态

More+
  • HTML5大前端 <高端班>

    开班时间:2020-11-16(北京)

    开班盛况

    开班时间:2020-12-07(深圳)

    预约报名
  • 大数据+人工智能 <高端班>

    开班时间:2020-09-14(北京)

    开班盛况

    开班时间:2020-11-09(北京)

    开班盛况
  • JavaEE分布式开发 <高端班>

    开班时间:2021-01-04(北京)

    预约报名

    开班时间:2020-07-20(北京)

    开班盛况
  • Python人工智能+数据分析 <高端班>

    开班时间:2020-07-20(上海)

    开班盛况

    开班时间:2020-09-21(上海)

    开班盛况
  • 云计算开发 <高端班>

    开班时间:2019-07-22(北京)

    开班盛况

    开班时间:2019-07-15(深圳)

    开班盛况
在线咨询
免费试听
入学教程
立即报名

Copyright 2011-2020 北京千锋互联科技有限公司 .All Right 京ICP备12003911号-5 京公安网11010802011455号