Java】【并发编程】入门知识

网友投稿 412 2022-05-30

一、基本简介

什么是并发

在Java中并发就是指多线程的进程环境,进程是系统进行资源分配和调度的独立单位,每一个进程都有它的内存空间和系统资源,在同一个进程内执行的多个任务就可以看作是多个进程,线程存在于进程内,进程负责分配调度线程,线程负责执行程序,多个线程就执行多个程序。

实际上,Java程序天生就是一个多线程程序,包含了:

分发处理发送给JVM信号的线程

调用对象的finalize清除方法的线程

清除相互引用reference的线程

main线程,也就是用户程序的入口,main线程里面还可以拥有很多的子线程

为什么需要多线程

如果没有多线程,若为了使程序并发执行,那么系统需要花费大量的时间在:创建进程-->撤销进程-->进程上下文切换调度,在这一过程中,需要的空间开销也非常大,执行效率也非常低(如下图);若在一个进程中执行多个线程,则上面的空间开销和时间花费将会大大较少,何乐而不为呢,多线程提高了系统的执行效率,充分利用多核CPU的计算能力,提高应用性能。

二、并发编程带来的问题

频繁的上下文切换问题

正如上图中的时间片,时间片使CPU分配给各个线程的时间,因为时间非常短,所以CPU需要不断切换线程,让我们觉得多个线程是同时执行的,时间片一般是十几毫秒;每次切换都需要保存当前线程的状态,以便进行恢复先前的状态。这个切换是非常耗性能的,过于频繁就无法发挥出多线程编程的优势了。那么该怎么解决这频繁的上下文切换的问题的,目前有大概几种解决方法,后面会详细讨论:

采用无锁并发编程:JDK8以前的concurrentHashMap采用的锁分段思想,不同线程处理不同段的数据,这样在多线程环境下可以减少上下文的切换时间。

采用CAS算法:JDK8以后的concurrentHashMap采用的是无锁CAS算法;利用Atomic和乐观锁,可以减少一部分不必要的锁竞争带来的上下文切换。

尽量减少线程的使用:避免创建不需要的线程,比如任务少,但是创建了很多的线程,这样会造成大量的线程都处于等待状态。

采用协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。

因此,并发累加未必会比串行累加的速度快,这上下文切换的问题在实际中是需要解决的。

线程安全问题(主要问题,也是我们程序开发关心的问题)

对线程编程中最难控制的就是临界区(共享内存的地方)的线程安全问题,稍微不注意就会出现死锁的情况,一旦产生死锁就会造成系统功能不可用。那么怎么解决这种问题呢,解决方法如下:

避免一个线程同时获取多个锁

避免一个线程在锁内部占用多个资源,尽量保证一个锁只占用一个资源

尝试使用定时锁,如使用lock.tryLock(timeOut),当超时等待时当前线程也不会阻塞

对于数据库锁,加锁和解锁必须在同一个数据库连接里(同一个事务),否则会出现解锁失败的情况

后面还有JMM内存模型在原子性、有序性和可见性带来的问题,比如数据脏读,内存泄漏等等问题,这是又该如何保证线程安全呢,这一方面是非常重要的,后面会详细讨论。

三、并发编程的相关概念

同步和异步

同步和异步通常用来形容方法的一次调用。

同步方法从被调用开始,调用者就必须等待被调用的方法结束后,调用者后面的代码才能继续执行。

异步方法指的是,调用者不管被调用方法是否完成,都会继续执行后面的代码,当被调用的方法完成后会通知调用者。

并发和并行

并发是指多个任务线程交替进行。

并行是指真正意义上的“同时进行”。

实际上,如果系统只有一个CPU,而使用多线程时,那么真实环境下时不能并行执行的,只能通过切换时间片的方式交替进行,完成并发执行任务,真正的并行只能出现在拥有多个CPU系统中。

阻塞和非阻塞

阻塞和非阻塞通常用来形容多线程间的相互影响。

阻塞是指如果一个线程占用了临界区的资源,那么其他线程需要这个资源的话就必须等待资源的释放,就会导致等待的线程挂起,这种情况就叫做阻塞。

非阻塞刚好跟阻塞相反,它强调的是没有一个线程可以阻塞其他线程,所有的线程都会尝试的向前运行。

临界区

临界区用来表示一种公共资源会共享数据,可以被多个线程使用,出于线程安全问题,如果一个线程占用了临界区的资源,那么其他线程就必须等待,知道临界区的资源被释放。

守护线程

守护线程是一种特殊的线程,是系统的服务线程,是专门为其他线程服务的,像垃圾回收线程就是守护线程,与之对应的是用户线程,用户线程作为系统的工作线程,守护线程的服务对象就是用户线程,当全部的用户线程执行任务完成之后,这个系统就没有什么需要服务的了,那么守护线程就没有对象需要守护了,那么守护线程就会结束,也就是说当一个java程序只有守护线程的时候,虚拟机就会退出了。

四、Java中的线程Thread类

参考看一下Thread类的源码注释,了解Java中的线程:

/**

1.一个Thread类对象代表程序中的一个线程,jvm是允许多线程的

* A thread is a thread of execution in a program. The Java

* Virtual Machine allows an application to have multiple threads of

* execution running concurrently.

*

2.每一个线程都有优先级,具有高优先级的线程优先于底优先级的线程执行,每一个线程都可以设置成一个守护线程,创建线程的时候,通过线程设置setDaemon(true)就可以设置该线程为守护线程,设置守护线程需要先于start()方法

* Every thread has a priority. Threads with higher priority are

* executed in preference to threads with lower priority. Each thread

* may or may not also be marked as a daemon. When code running in

* some thread creates a new Thread object, the new

* thread has its priority initially set equal to the priority of the

* creating thread, and is a daemon thread if and only if the

* creating thread is a daemon.

*

2.只有当一个Java程序只存在守护线程的时候,虚拟机就会退出,让虚拟机不继续执行线程的方法有:

2.1调用system.exit方法.

2.2所有非守护线程都处于死亡状态(只有守护线程)或线程运行抛出了异常

注意:在线程启动前可以将该线程设置为守护线程,方法是setDaemon(boolean on)

使用守护线程最好不要在方法中使用共享资源,因为守护线程随时都可能挂掉

在守护线程中产生的线程也是守护线程

* When a Java Virtual Machine starts up, there is usually a single

* non-daemon thread (which typically calls the method named

* main of some designated class). The Java Virtual

* Machine continues to execute threads until either of the following

* occurs:

*

    *

  • The exit method of class Runtime has been

    *     called and the security manager has permitted the exit operation

    *     to take place.

    *

  • All threads that are not daemon threads have died, either by

    *     returning from the call to the run method or by

    *     throwing an exception that propagates beyond the run

    *     method.

    *

*

3.创建线程的方式有两种(重写Runnable接口的run()方法):

3.1创建子类并继承Thread 类,同时重写run()方法(因为Thread类实现了Runnable接口)

3.2创建子类并实现Runnable接口,同时重写run()方法

下面有例子:

* There are two ways to create a new thread of execution. One is to

* declare a class to be a subclass of Thread. This

* subclass should override the run method of class

* Thread. An instance of the subclass can then be

* allocated and started. For example, a thread that computes primes

* larger than a stated value could be written as follows:

*


*     class PrimeThread extends Thread {

*         long minPrime;

*         PrimeThread(long minPrime) {

*             this.minPrime = minPrime;

*         }

*

*         public void run() {

*             // compute primes larger than minPrime

*              . . .

*         }

*     }

*


*

* The following code would then create a thread and start it running:

*

*     PrimeThread p = new PrimeThread(143);

*     p.start();

*

*

* The other way to create a thread is to declare a class that

* implements the Runnable interface. That class then

* implements the run method. An instance of the class can

* then be allocated, passed as an argument when creating

* Thread, and started. The same example in this other

* style looks like the following:

*


*     class PrimeRun implements Runnable {

*         long minPrime;

*         PrimeRun(long minPrime) {

【Java】【并发编程】入门知识

*             this.minPrime = minPrime;

*         }

*

*         public void run() {

*             // compute primes larger than minPrime

*              . . .

*         }

*     }

*


*

* The following code would then create a thread and start it running:

*

*     PrimeRun p = new PrimeRun(143);

*     new Thread(p).start();

*

*

4.每个线程都有一个名称,如果没有会在创建的时候自动生成一个,除非指定为null。

* Every thread has a name for identification purposes. More than

* one thread may have the same name. If a name is not specified when

* a thread is created, a new name is generated for it.

*

* Unless otherwise noted, passing a {@code null} argument to a constructor

* or method in this class will cause a {@link NullPointerException} to be

* thrown.

*/

五、总结

我们需要了解并发,为什么需要并发,还必须知道并发的优缺点,同时清楚使用并发编程之后所带来的问题:频繁上下文切换问题和线程安全问题等等,后面在并发编程的时候就朝着这些问题去编程,尝试解决这些问题,让并发编程发挥出真正的作用。

理解Java并发的关键点在于理解它的两大核心(JMM内存模型【工作内存和主内存】和happes-before规则【八大规则】)以及三大特性:原子性、可见性、有序性

Java 任务调度

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:【JAVA项目管理】【Maven】一篇搞定maven
下一篇:人从众,越来越多的企业基于华为云走向全局创新
相关文章