java线程相关问题和各种锁 小鱼儿 2022-05-25 22:56 116阅读 0赞 # JAVA中线程相关问题 # ## 1.线程基础 ## 进程:进程是指正在进行的程序。当程序进入内存中执行时就变成了进程,具有一定的独立性。 线程:线程是进程中的执行单位,负责进程的执行。一个进程中有多个线程,多个线程在内存中通过分时调度和抢占式调度来执行。 单线程:又称为同步线程(与线程同步不同,线程同步指保证线程安全),代码自上至下执行。 多线程:又称为异步线程,多线程具有三大特性:原子性、可见性、有序性。 原子性:同一条线程中的数据处理,要么都进行,要么都不执行,要保证线程执行过程中所有数据保持一致。 可见性:一条线程修改共享变量值,其他线程立即得知这个修改后的值。(见第4点多线程第一段详解) 有序性:一条线程中执行顺序按照代码自上而下。 关于线程有许多知识点,如线程状态、线程池、多线程的安全问题与并发以及锁对象。 创建线程的几种方法:1.继承Thread,重写run()方法 2.实现Runable接口,重写接口中的run()方法,newThread(传入Runable接口实现类) 3.匿名内部类,new Thread(重写run方法)、newRunable(重写run方法) 4.Callable<T>,类似Runable,不过会返回处理结果,T为返回结果类型 5.线程池提供线程 ## 2.线程状态 ## 1.新建状态:创建线程后,尚未使用线程执行代码。 2.就绪状态:广义的就绪状态是指新建线程start之后即将进入运行的状态 3.阻塞状态:主动休眠和等待状态、被动被阻塞状态都是阻塞状态 4.运行状态:线程获得CPU运行时间,进入执行的状态。 5.死亡状态:线程执行结束或者未catch异常终止run方法。 ![70][] ## 3.线程池 ## 线程池可以看作是一种使用工厂设计思想的简易框架,几乎所有异步并发的多线程程序都可以使用线程池。 线程池作用:1.降低资源消耗,通过重复利用已创建的线程降低线程的创建和销毁造成的内存消耗。 2.提高响应速度,当任务到达时,不需要等待线程创建就能立即执行。 3.提高线程的客观理性,线程不能无限制创建,会消耗系统资源并降低系统的稳定性,使用线程池可以对线程进行统一分配、调优和监控。 Java为线程池提供了一个类,java.util.concurrent.ThreadPoolExecutor,属于线程池的核心类。其实现了Executor, ExecutorService两个接口,ExecutorService用来执行提交的任务,Executor用来对工厂进行配置。 线程池的相关配置问题: A.核心线程池和线程池最大大小。ThreadPoolExecutor通过setCorePoolSize(int)设置核心线程数和setMaximumPoolSize(int)设置循序最大线程数来调整线程池大小。当CorePoolSize<MaximumPoolSize,当队列满时会创建新线程,当CorePoolSize=MaximumPoolSize时,线程池为固定大小。 B.按需构造。默认情况下,核心线程最初只是在新任务需要时才创建和启动,可以使用方法品restartCoreThread()或prestartAllCoreThreads()对其进行动态重写。 C.创建新线程。线程池创建新线程使用Executors.defaultThreadFactory()的方法,新线程默认具有NORM\_PRIORITY优先级和非守护进程状态。 D.线程池中线程活动时间。线程池中的非核心线程会在空闲时间超过某个时间时进行销毁,用来减少非活动状态下对系统资源的消耗,如果核心线程队列满时,自动创建新线程。 E.排队。如果运行线程小于核心线程数,Executor首选添加新线程,不进行排队;如果运行线程等于或多余核心线程数,Executor首选请求加入队列,不添加新线程;如果无法请求加入队列,则创建新线程,当创建的线程数大于线程池最大线程数时,任务将被拒绝。 Executor封装了四种线程池:CachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。FixedThreadPool 创建一个定长线程池,可控制线程最大并发数超出的线程会在队列中等待。ScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。SingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO, 优先级)执行。 ## 4.多线程并发 ## JVM中存在一个主存区,主存区中的数据对所有线程进行共享,同时每个线程又拥有自己的工作内存,工作内存的数据是从主存区中拷贝,线程对各自工作内存中的数据进行操作,然后传递至主存区中。线程之间是不能直接相互访问的,依赖主存区进行。某个线程A从主存区中拷贝到数据X,并在自己的工作内存中对X进行处理,但并未及时将处理后的X更新至主内存中,线程B在内存中读取到了主内存中原始的数据X,此时线程B并不知道线程X已存在改变。这种情况称为线程安全问题。 ![70 1][] 为解决上述的线程安全问题,java提出了线程同步机制:同步代码块和同步方法以及一个接口Lock。 同步代码块: Synchronized(锁对象)\{可能产生线程安全的代码\} 同步代码块中的锁对象可以是任何对象(不要和接口Lock搞混),但要解决多线程的安全问题,要使用同一锁对象。 同步方法: public Synchronized void method()\{可能产生线程安全的代码\} 同步方法中的锁对象是this 接口Lock:锁定lock()和取消锁定unlock()出现在不同作用范围中时,相较于同步代码块和同步方法更灵活,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁定。 死锁:是指多个以上进程在执行的过程中,由于抢占资源或彼此通信造成的一种祖册现象,若无外力作用,它们将无法推进下去。此时成系统处于死锁状态,或产生了死锁,这种相互等待的进程称为死锁进程。 ## 5.锁机制 ## 1.悲观锁:悲观地认为每次操作都会造成更新丢失问题,也就是说认为别人会拿去修改,一次每次查询数据时都会加上排它锁。这样别人想拿,就会被拒绝直到它拿到锁。使用方法:SQL语句后面添加for update,例如select \* from order for update ;原理:只允许一个连接进行操作,当一个线程进入后会锁住,别的线程要等待锁释放才能使用。效果较低。 2.乐观锁:乐观的认为每次查询都不会造成更新丢失,利用版本字段控制。原理:每个数据库都有一个版本号version,当读取数据时,会将version字段一同读出,数据每更新一次,version值就+1,当我们提交更新时,会判定version是不是我们取数据时候的version,如果不是就认为数据过期,会更新失败。乐观锁的第二种实现方法是在表中增加一个类似version的时间字段(这个字段可以不用专门增加,使用表中原有字段也可),原理同version相同。 3.重入锁:以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他线程是不可以的。原理:通过为每个锁关联一个请求计数和一个占有它的线程,当计数为0时,认为锁是未被占有的,线程请求一个未被占有的锁时,jvm将记录该线程,并将计数器置为1,如果同一线程再次请求这个锁时,计数器将+1,每次线程退出同步块时,计数器值将为0,锁被释放。 4.读写锁(一种互斥锁):两个线程同时读一个资源没有问题,但如果一个线程想要更新一个资源,其它线程就不能进行读写操作,也就是说读读共存,读写和写写不共存。 5.CAS无锁:CAS无锁机制是不给一段代码加锁,效率比有锁高。CAS体系有三个参数,V/E/N。V表示要更新的数据,E表示的预期值,N表示新值。当一个线程获取一个数据V,线程将操作后的数据V改为数据N,当V=E时,将数据V修改成数据N,如果V!=E时,当前线程不进行更新或者重新进行操作。举个栗子,数据库有数值=1,线程A将数值改成2,提交时线程的预期值是数据库里的数值为1,才能提交成功,如果数据库数值不为1,则说明有其它线程更改过数据,提交不成功。 6.自旋锁:自旋锁与互斥锁类似,不同点是互斥锁机制中,锁的执行线程执行了,等待调用线程就休眠了,而自旋锁机制中,等待调用线程会在那里等着(将等待调用线程放置在空循环中,让它在那里转圈圈,一圈两圈三四圈),直到锁的执行线程释放了锁。自旋锁比较浪费资源。 7.分段锁:分段锁是锁的一种设计。相当于将一条数据的某一段进行加锁,细化锁的粒度,如果要获得整条数据的操作信息,就需要获取所有的分段锁才能统计。 锁的分类: 按锁的获取顺序来分:公平锁和非公平锁。公平锁是指多线程按照申请锁的顺序来获取锁;非公平锁指多个线程获取锁的顺序不是按照申请锁的顺序进行。非公平锁吞吐量比公平锁大。 按锁被线程持有的权限分:独享锁和共享锁。独享锁是指锁只能被一个线程持有;共享锁指该锁可以被多个线程持有。下面的读写锁中,读读锁为共享锁,读写锁为独享锁。 按照看待并发同步的角度分:悲观锁和乐观锁 按照锁的状态分(只针对Synchronized):偏向锁、轻量级锁、重量级锁。偏向锁指一个线程获取锁时,下一次会自动获取锁,可重入锁机制就属于偏向锁,用来降低获取锁的代价。轻量级锁指偏向锁被另一个线程获取,其它线程会通过自旋锁机制尝试获取锁。重量级锁指轻量级锁中,在自旋的线程自旋到一定次数后就会阻塞,该锁就成了重量级锁。 [70]: /images/20220526/942bce2944e141da87e7d003fb850f44.png [70 1]: /images/20220526/03bff81f5bf04549bca16c5ed694b8b8.png
相关 Java多线程问题:死锁和活锁实例展示 在Java多线程编程中,死锁和活锁是两种常见的并发问题。下面我会为你提供这两种情况的实例。 1. 死锁实例: 假设有两个线程A和B,它们的操作如下: - 线程A 刺骨的言语ヽ痛彻心扉/ 2024年09月10日 23:09/ 0 赞/ 36 阅读
相关 Java 面试题 (三) --------- Java 线程、锁相关 1、请谈谈 ReadWriteLock 和 StampedLock ReadWriteLock包括两种子锁 ① ReadWriteLock ReadWriteLock 可 川长思鸟来/ 2024年03月31日 15:49/ 0 赞/ 25 阅读
相关 iOS 多线程 各种锁 在iOS中有几种方法来解决多线程访问同一个内存地址的互斥同步问题: 方法一,@synchronized(id anObject),(最简单的方法) 会自动对参数对象加锁, - 日理万妓/ 2022年08月21日 05:49/ 0 赞/ 136 阅读
相关 java线程死锁问题 Synchronized关键字 Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。 1. 当两个并发线程 野性酷女/ 2022年07月27日 14:48/ 0 赞/ 164 阅读
相关 iOS多线程-各种线程锁 线程安全是怎么产生的 常见比如线程内操作了一个线程外的`非线程安全变量`,这个时候一定要考虑线程安全和同步。 - (void)getIamgeName:(NSM Dear 丶/ 2022年06月15日 11:13/ 0 赞/ 204 阅读
相关 java关于多线程相关问题 在典型的Java面试中, 面试官会从线程的基本概念问起, 如:为什么你需要使用线程, 如何创建线程,用什么方式创建线程比较好(比如:[继承thread类还是调用Runnable 青旅半醒/ 2022年06月03日 05:43/ 0 赞/ 166 阅读
相关 线程相关问题 60、java中有几种方法可以实现一个线程? 用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用? 答: 有两种实现方法,分别是继承Th 小灰灰/ 2022年05月28日 08:25/ 0 赞/ 119 阅读
相关 java线程相关问题和各种锁 JAVA中线程相关问题 1.线程基础 进程:进程是指正在进行的程序。当程序进入内存中执行时就变成了进程,具有一定的独立性。 线程:线程是进程中的执行单位,负责 小鱼儿/ 2022年05月25日 22:56/ 0 赞/ 117 阅读
相关 Java多线程中 的各种锁 > 学习 java 多线程时,最头疼的知识点之一就是 java 中的锁了,什么互斥锁、排它锁、自旋锁、死锁、活锁等等,细分的话可以罗列出 20 种左右的锁,光是看着这些名字就足 ゞ 浴缸里的玫瑰/ 2021年09月29日 15:00/ 0 赞/ 359 阅读
还没有评论,来说两句吧...