美国上市公司,专注Java培训22年

Java基础教程之 Java 多线程并发


1 Java 线程实现/创建方式

继承 Thread 类

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

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();

} }

ExecutorService、Callable、Future 有返回值线程

有返回值的任务必须实现 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 框架实现的,该框架中用到了 Executor,Executors,ExecutorService,ThreadPoolExecutor ,Callable 和 Future、FutureTask 这几个类。

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 时,多余的空闲线程的存活时间,即多次时间内会被销毁。

unit:keepAliveTime 的单位。

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

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

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

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

JDK 内置的拒绝策略如下:

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

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

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

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

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

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

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

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

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

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

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

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

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

希望我的分享对你有所帮助,如果你想提升自己的Java技能不放现在达内Java培训机构,现在填写下面的表单即可有机会获得达内Java培训班免费体验课程。

免责声明:内容来源于网络,若涉及侵权联系尽快删除!


【免责声明】本文部分系转载,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,如涉及作品内容、版权和其它问题,请在30日内与我们联系,我们会予以重改或删除相关文章,以保证您的权益!

Java开发高端课程免费试学

大咖讲师+项目实战全面提升你的职场竞争力

  • 海量实战教程
  • 1V1答疑解惑
  • 行业动态分析
  • 大神学习路径图

相关推荐

更多
  • eclipse怎么建立Java项目?
    eclipse怎么建立Java项目?
    eclipse怎么建立Java项目?在使用Eclipse建立Java项目时,可以遵循以下步骤: 详情>>

    2024-05-10

  • Java关键词汇总-Java关键字有哪些及其作用?
    Java关键词汇总-Java关键字有哪些及其作用?
    Java关键词汇总-Java关键字有哪些及其作用?Java语言中的关键字(Keywords)是Java编程语言的核心组成部分,它们具有特定的意义,并被用于表示基本数据类型、控制语句、访问权限等。Java关键字总数大约有50个左右,下面列出了一些常用的Java关键字及其作用: 详情>>

    2024-04-08

  • Java编程工具用哪个最好
    Java编程工具用哪个最好
    Java编程工具用哪个最好?Java编程工具的选择取决于个人的偏好、项目需求和开发环境。以下是一些常用的Java编程工具: 详情>>

    2024-04-03

  • 好用的Java编写软件
    好用的Java编写软件
    Java是一种广泛使用的编程语言,因其跨平台、安全性和稳定性而受到许多开发者的喜爱。Java被用于开发各种类型的软件,从桌面应用程序到企业级服务器端应用程序。以下是一些常见的、用Java编写的软件示例: 详情>>

    2024-04-02

  • Java开班时间

    收起