Java多线程小记,你学会了吗?

在一个进程中可以有多个执行单元同时运行,来同时完成一个或者多个程序任务,这些执行单元被称为线程。当启动一个java程序系统就会创建一个进程,该进程也会创建一个线程来运行main方法中的代码。操作系统中的进程都至少有一个线程。,代码按照调用顺序依次往下执行不会出现代码交替运行的就叫做单线程程序,实现代码交替运行效果的叫做多线程程序。多线程程序运行时每个线程之间都是独立的,可以并发执行。虽然称可以并发执行但是实际上线程和进程一样都是由CPU控制轮流执行的,只是CPU的速度快让人感觉是同时执行的感觉。所以说多线程交替执行代码。,java对多线程的支持主要有三种方法:,Thread位于 java.lang包。,优势:代码简单,缺陷:一个类只能继承一个父类,不利于代码拓展,不能获取线程的返回值。,主要步骤:,代码示例:,从输出内容看有两个线程在交互运行,但实际上运行这段代码之后会产生一个!进程 !,一个java的进程,这个java进程里面包含三个线程,其中两个就是我们定义的t1、T2,还有一个由main方法开启的主线程,只不过启动了两个线程实例时候没有做其他动作。,优势:代码简单,不用继承。,缺陷:不能获取线程的返回值。,主要步骤:,代码示例:,因为run方法是Runnable接口唯一的抽象方法,Runnable就属于函数式接口,可以使用Lambda表达式来实现Runnable的线程实例。,优势:代码简单,不用继承,有返回值,主要步骤:,代码示例:,Callable接口实现类和Runnable接口一样都要使用Thread类来实现多线程,不同的是,Callable传入的是!Runnable的子类 !FutureTask的实例对象,而我们在FutureTask<Object>ft1 = new FutureTask<>(m1);这一步时把Callable接口实现类给封装进来,这样Callable接口实现类就可以实现返回值了。,尽量采用Runnable或者Callable来实现多线程操作。因为相对于Thread,Runnable和Callable有以下几点优势:,每张票都被卖了两次,这显然不合理。如果使用Runnable或者Callable,就可以使用同一个实现类创建两个Thread线程实例,二者访问的都是同一个资源就不会出现Thread的情况。,对于java程序而言,只要有一个前台线程在运行那么这个进程就不会结束,相反的如果一个进程只有后台线程运行,那么这个进程就会结束。新创建的线程默认都是前台线程,如果在某个线程启动(调用start方法)之前调用setDaemon(true)语句,就可以把这个线程设置为后台线程。,代码示例:,1、NEW 新建状态,和其他java对象一样,由jvm分配了内存,还是不能运行,没有表现出任何线程的动态特征。,2、RUNNABLE 可运行,新建状态下的线程对象调用start方法。内部细分为两种,线程可以在二者之间相互转换。,READY 就绪:线程对象调用start方法之后等待JVM调度,并未运行。,RUNNING 运行:获得JVM调度,如果由多个CPU就允许多个线程并行运行。,3、BLOCKED 阻塞,处于运行状态的线程失去CPU执行权从而暂停运行进入阻塞状态,此时JVM不会给它分配CPU,直到进入就绪状态。,线程一般在以下情况会阻塞:,4、WATING 等待,处于运行的线程调用了无时间参数限制的方法(wait、join…)就进入等待状态。处于等待的线程不能争夺CPU使用权,必须等待其他线程执行特定操作之后才可以继续争夺cpu使用。,5、TIMED_WAITING 定时等待,运行线程调用了有时间参数限制的方法(sleep…),处于定时的等待的线程也不能立即争夺CPU使用权。,6、TERMINATED 终止,线程的run、call方法正常执行完毕或者线程抛出一个未捕获的异常、错误,都会导致线程进入终止。进入终止之后就没有运行资格,不能转换到其他状态,声明周期结束。,定义:程序中的多线程时并发执行的,却不是在统一时间执行的。若想被执行就需要获得CPU使用权。JVM会按照特定的机制为程序中的每个线程分配CPU使用权,这种机制叫线程的调度。,分时调度模型:让所有线程轮流获得cpu使用权,平均分配每个线程占用cpu的时间篇。,抢占式调度模型:让可运行池中所有就绪的线程争夺cpu使用权。优先级高的线程获取cpu使用权的概率大于优先级低的线程。,JVM默认采用抢占式调度模型。,对线程进行调度最简单的方式就是设置线程的优先级。线程优先级使用1~10之间的整数表示,数字越大优先级越高。还可以使用Thread类中的三个静态常量表示:,修改线程的优先级,代码示例:,Thread类提供了一个静态方法sleep,可以让线程进入定时等待。sleep方法会抛出InterruptedException。,代码示例:,yield方法与sleep不同,yield不会阻塞进程,它只是将运行状态的线程转换为就绪状态使其被重新调度一次。java采用的抢占式调度,不能保证让步后立即就执行其他线程。,代码示例:,在线程中调用其他线程的join方法时,此线程会被阻塞知道join的线程被执行完成。,代码示例:,线程安全问题:当多个线程同时去访问同一个资源时会导致一些安全问题,比如线程A访问资源c时,资源c的值为1,之后线程A休眠了500毫秒,在此休眠期间线程B去访问了资源c,导致资源c的值变成0,这是休眠结束的线程A再去执行时资源c的值已经变化。为了解决这样的问题就出现了多线程同步。,多线程同步:限制某个资源在同一时刻只能被一个线程访问。,同步代码块是多线程同步的手段之一,当多个线程使用同一个资源时,将处理资源的代码放置在一个用synchronized关键字修饰的代码块中,这段代码块叫做同步代码块。,原理:同步代码块的关键在于lock,lock是一个锁对象,只有lock的标志位为1时线程才能执行同步代码块。当线程执行到同步代码块时先检查lock标志位,默认为1,线程会执行同步代码块同时lock的值置为0,这时其他线程就发生阻塞不能执行同步代码块中的代码。等线程执行完同步代码块之后lock又被置为1,以此循环往复。,重点:锁对象的类型可以时任意类型,但是各个线程使用的锁对象必须是同一个。也就是创建锁对象的代码不可以写在run方法,否则每个线程运行都会创建一个自己的锁对象,形同虚设。,代码示例:,小结:,对比上面两段代码的输出结果,在使用synchronized 时,多运行几次就可以看到输出结果可能会有0,或者同一张票在两个窗口都被卖了一次。相较于使用synchronized 时,可以发现输出都是同一个窗口在卖,因为在一个线程进入同步代码块之后另一个线程就阻塞了,即使当前线程使用sleep休眠但是lock标志位任然还在0所以另一个线程无法进入执行。,synchronized 不仅可以修饰代码块,也可以修饰方法。,使用:定义同步方法,然后再创建线程对象的run方法中直接调用同步方法就行。,原理:再使用同步代码块时需要定义锁对象,而使用同步方法就没有这样的问题。同步方法和同步代码块的原理一样,只不过同步方法的锁对象就是调用该方法的对象,就是this指向的对象。同步方法被所有线程共享,同步方法所在的对象相对于所有线程而言是唯一的,当一个线程进入同步方法时其他线程也不能进入同步方法执行了。,synchronized 使用一种封闭式锁机制,优点是使用起来非常简单,同时也有一些缺点,例如无法中断正在等候获得锁的线程,无法通过轮询获得锁等等。,JDK5开始增加了Lock 锁,Lock 锁功能与synchronized 基本相同,但是Lock锁可以让线程再持续获得锁失败之后不再继续等待,使用上也比synchronized 更灵活。,使用:,代码示例:,两个正在运行的线程都在等待对方的锁,从而造成程序的停滞现象称为死锁。两个线程都需要对方占用的锁,但是二者又无法释放自己拥有的锁,于是双方都处于挂起状态。,为了控制多个线程按照一定的顺序轮流执行,就需要让线程之间进行通信保证线程任务协调进行。,线程通信常用方法:,这些方法位于Object类中可以直接使用。,代码示例:,线程对象的使用需要消耗大量的内存,使用线程池来创建多线程可以进一步优化线程管理。java主要提供了一个接口和一个类来实现线程池管理。,JDK5开始在 java.util.concurrent 包下增加了Executor 接口及其子类。,使用:,代码示例:,使用Callable接口实现多线程时会用到FutureTask类对线程的返回值进行管理,由于FutureTask在获取返回结果时是通过阻塞或者轮询的方式进行耗费太多的资源。JDK8中对FutureTask增加了一个 !函数式异步编程辅助类CompletableFuture !该类同时实现Future接口和CompletionStage接口(JAVA8新增的线程任务完成结果接口)。,获取CompletableFuture 对象:,runAsync 和 supplyAsync 的本质区别在于获取的 CompletableFuture 对象是否带有计算结果(类似Runnable和Callable的区别)。带有 Executor 参数的方法指定传入的线程池执行器来执行多线程,没有的默认使用ForkJoinPool.commonPool() 进行线程池的多线程管理。,代码示例:,

文章版权声明

 1 原创文章作者:cmcc,如若转载,请注明出处: https://www.52hwl.com/19054.html

 2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈

 3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)

 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023年3月5日 上午12:00
下一篇 2023年3月7日 下午10:34