当前位置: 主页 > JAVA语言

实战java高并发程序设计-java并发编程实战目录

发布时间:2023-02-09 22:19   浏览次数:次   作者:佚名

说到高并发,这几年几乎成了编程界流行的网络名词。 离不开它,随着互联网的飞速发展,尤其是电子商务平台应用的迅猛发展,互联网服务内容越来越丰富,用户越来越多。 淘宝、天猫、京东。 音频等几乎成为了普罗大众每天必用的应用。 然而实战java高并发程序设计,在这些应用中看到的“天猫双11”、“京东618”、“商品秒杀”、“抢火车票”等,往往会在短时间内产生大量的并发访问和流量。 天猫双11的崩盘我不是没见过! 那么如何解决这个高并发问题的第一个基础就是能够玩好线程,那么接下来的内容我们来学习一下线程。

1、为什么会有线程?

举个简单的例子,假设我们正在使用百度网盘应用,我们要使用百度网盘的上传下载功能。 如果没有线程,那么我们的操作只能是这样:上传文件的时候不能做其他的事情,需要上传成功才能下载其他的东西,上传文件只能一个一个上传,那将是非常糟糕的体验。 那么我们想要百度网盘可以同时上传下载,并且可以多次上传下载怎么办呢? 没错,使用线程!

现在的操作系统不管是windows还是linux系列,基本上都是多用户多任务的操作系统,多任务是通过多线程来实现的。 多任务也称为并发。

2.操作系统中进程和线程的概念

说到线程,就不得不先提到进程。 很多人常常认为进程就是程序,那么它是一回事吗? 让我们看一下流程的定义。

·工艺概览

在系统中能够独立运行的程序称为进程。 进程是CPU资源分配的最小单位;

例如:windows进程

java并发编程实战目录_java并发编程实战pdf_实战java高并发程序设计

每个进程都有自己独立的一块内存空间。 单核CPU是单进程处理,即一次只能处理一个进程,但系统可以给每个进程分配有限的CPU执行时间(称为CPU时间片),CPU执行一定的这个时间段的进程,然后跳转到下一个时间段的另一个进程去执行,因为CPU切换速度太快了,远远超出了我们肉眼的识别能力,所以我们看到很多进程好像在同时运行时间。 多核CPU可以实现多个进程同时执行,但是由于调度问题,一个核可能会在一个时间片内调用多个进程。

· 线程概述

实战java高并发程序设计_java并发编程实战目录_java并发编程实战pdf

线程是进程中完成一个进程的执行任务。 它是进程中的一条执行路径,与进程共享一块内存空间。 线程是程序中最小的执行单元。

线程本身不能单独存在,它需要在进程中运行。 一个进程可以有多个线程(例如:Java程序基本上有main主线程和GC线程(垃圾收集器)等两个或多个线程),同一个进程的所有线程共享进程的资源。

3. Java中的线程实现

通过前面的介绍,我们知道了线程,那么线程在Java中是如何实现的呢? Java中线程的三种主要实现方式:

· 继承Thread类

实现Runnable接口

实现可调用接口

前两种实现方式比较通用,类和接口也在java.long包中。 第三个 Callable 在 java.util.concurrent 包中。 我们会在后面介绍它的实现和区别。 我们先来看一下。 下面是前两个实现。

(1)继承Thread类

首先,我们看到 Thread 类也实现了 Runnable 接口。

java并发编程实战目录_java并发编程实战pdf_实战java高并发程序设计

java并发编程实战pdf_java并发编程实战目录_实战java高并发程序设计

这样我们就可以直接创建Thread对象并使用了。

使用步骤:

A.继承Thread类或者直接创建Thread对象

B.在run方法中实现线程任务代码

C、调用Thread的start方法启动线程

java并发编程实战pdf_实战java高并发程序设计_java并发编程实战目录

分析:以下方法不需要定义类,使用更简单,适用于调用次数较少的情况。 另外,虽然run方法是在Thread类线程启动后执行的,但是线程的start方法是start方法!

start方法和run方法的区别:

run方法只是纯Thread类的一个普通方法,但是在线程启动的时候会被调用。 如果只使用thread.run(),只表示我们调用了thread的run方法,并没有开启新的线程。 代码仍然在主线程main中执行。

java并发编程实战目录_java并发编程实战pdf_实战java高并发程序设计

调用start方法表示一个新的线程已经创建,线程进入就绪状态。 当线程被CPU时间片处理完后实战java高并发程序设计,Java虚拟机会调用线程的run方法,执行里面的任务代码。 任务代码执行完毕后,线程Finish。 另外,start启动的新线程是一个独立的执行任务,后面的代码可以继续执行,不需要等待线程执行完。

使用 Thread 类的限制:

Thread类的实现虽然简单,但是由于Thread是一个类,Java中的类只能进行单继承,所以扩展线程的能力较差。

(2)实现Runable接口

Runnable 是一个功能接口(用@FunctionalInterface 装饰并且只有一个抽象方法)。 定义很简单,只有一个run方法。 源代码如下:

实战java高并发程序设计_java并发编程实战目录_java并发编程实战pdf

之前我们也介绍过,Thread类其实实现了Runable接口,启动线程执行任务代码的run方法其实就是Runnable接口的一个方法。 所以我们可以通过实现Runable接口,配合Thread类来实现线程的扩展使用。

使用步骤:

A.定义一个实现Runnable接口的类

B.在run方法中实现线程任务代码

java并发编程实战pdf_java并发编程实战目录_实战java高并发程序设计

C、通过Thread(Runnable r)构造方法传入Runnable接口实例对象,调用start方法启动线程

java并发编程实战目录_实战java高并发程序设计_java并发编程实战pdf

分析:

使用Runnable接口的实现,由于接口可以通过多种方式实现,Runnable接口可以被更多的类实现,扩展性比Thread强。 另外,由于Runnable是传入Thread构造函数创建的线程,所以Runnable需要依赖Used with Thread类,多个Thread对象可以使用同一个Runnable实例。

Runnable的Lambda表达式的使用:

Java8 提供了 Lambda 表达式。 当我们使用Lambda表达式创建Runable实现类实例时,我们不需要定义类,变得更加方便。 代码实现如下:

java并发编程实战目录_java并发编程实战pdf_实战java高并发程序设计

4、多线程经典案例——卖票案例

虽然在开发过程中可以通过继承Thread类并实现Runnable接口来实现线程,但是继承Thread类也有局限性。 更容易扩展Runnable接口的实现,Rnnnable接口的实现实例可以被多个Thread对象共享,更方便解决一些多线程处理资源。 我们以售票为例来说明:

我们有3个售票窗口,一共有100张票,那怎么实现三个窗口同时售票,卖100张票呢?

java并发编程实战pdf_java并发编程实战目录_实战java高并发程序设计

(1)Thread类的实现

java并发编程实战目录_java并发编程实战pdf_实战java高并发程序设计

运行结果:

实战java高并发程序设计_java并发编程实战pdf_java并发编程实战目录

综上所述:

最后发现每个窗口卖100张票,一共卖了300张,但是一共只有100张。 显然,继承Thread类是不方便的,因为t1、t2、t3这三个窗口线程不能共享100张票。 资源,所以他们都卖了 100 份。

Runnable接口的实现

java并发编程实战目录_实战java高并发程序设计_java并发编程实战pdf

运行结果:

java并发编程实战目录_实战java高并发程序设计_java并发编程实战pdf

综上所述:

我们发现使用Runnable接口可以让三个窗口线程t1、t2、t3同时卖100张票,而不是每个线程卖100张票,因为t1、t2、t3都使用同一个Runnable实例。 显然,Runnable接口的实现可以实现100票资源的共享,但是我们通过运行结果会发现一个比较迷惑的问题,就是无论程序运行多少次,总是有两三个线程在卖同一张票! 这是什么原因? 其实这就是所谓的“多线程并发访问不安全问题”! 在后面的章节中,我们将介绍为什么多线程访问对象是不安全的。