![](https://img-blog.csdnimg.cn/20190918140158853.png?x-oss-process=image/resize,m_fixed,h_224,w_224)
多线程
多线程的学习笔记。
何怀逸
这个作者很懒,什么都没留下…
展开
-
EasyExcel+多线程实现大数据量
第二点其实也不用担心,只需要把BATCH_COUNT参数传进new PageReadListener()的构造函数就可以了,这样每次读取数据到内存中的数量就是BATCH_COUNT条,默认BATCH_COUNT=100,我这里设置成一千,最后不足一千也会读取完然后入库。在没有任何校验逻辑的情况下,导入了90万数据,时间大概半分钟多一点,速度还是很快的。这里应该是会涉及到数据库连接池的问题,需要限制连接的数量,不能将资源都分给文件上传。2、批量入库问题,如果最后一批数据不足临界值怎么解决。原创 2024-01-11 09:39:31 · 1236 阅读 · 0 评论 -
多线程中锁的优化
锁的再设计和锁优化。原创 2024-01-10 09:17:58 · 378 阅读 · 0 评论 -
总结多线程的各种锁
举例:当我们使用非公平锁时,如果线程A释放锁之后,本来应该唤醒队列中的线程B,此时却被没有进入到阻塞队列的线程C截胡了,线程C竞争到锁之后就直接执行业务逻辑,就不需要再进入到阻塞队列了,这样就省去了后续再唤醒线程C而带来的性能开销,当然,这对于原本需要唤醒的线程B是不公平的,但是却提升了锁竞争的性能。悲观锁在更新数据时,认为其它线程会更新数据,因此会加锁,别的线程想要修改数据,就必须获取锁,获取不到锁就被阻塞。线程在抢占到一个互斥锁资源后,在释放锁之前,再去竞争同一把锁时,无需等待,只需要记录重入的次数。原创 2024-01-09 19:37:01 · 1262 阅读 · 0 评论 -
Volatile变量
当变量被Volatile变量声明后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作和其他内存操作一起重排。变量的值时,该线程会对这个变量进行写操作,然后插入一个写内存屏障,接着其他线程对这个变量的读操作会被重排序到写内存屏障之后。在没有使用volatile的情况下,操作系统指令重排的情况下,可能会造成2和3顺序交换,就会导致一个没有初始化的对象引用暴露出来。确保变量的更新能够及时的通知到其他线程,读取时能获得该变量的最新值。时,JVM会确保对这个变量的读写操作具有原子性。原创 2024-01-09 10:44:10 · 411 阅读 · 0 评论 -
显示锁-ReentrantLock
非公平锁则是让线程可以”插队“,在非公平锁的情况下,新来的线程只有在锁被别的线程持有时,才会进入队列,不然不会进入队列,而是立即参与竞争,如果竞争成功,则省去了线程状态切换的开销,这说明了某些情况下,非公平锁比公平锁性能要好。但是,非公平锁会出现饥饿现象。只有在内置锁无法满足要求的情况下,才应该去考虑ReentrantLock,ReentrantLock作为一种高级的备选方案,选择它的原因是因为它的高级功能。但是在激烈竞争的情况下,线程从内核态转变为用户态存在着严重的延迟,此时会带来性能的影响。原创 2024-01-09 09:13:24 · 372 阅读 · 0 评论 -
多线程-synchronized详解
当某个线程尝试获取其它线程已经持有的锁时,尝试获取锁的线程就会阻塞。但是,内置锁synchronized是可重入的,如果某个线程尝试获取由自己已经持有的锁时,请求成功。另外,关键字synchronized修饰的方法,整个方法体就是同步代码块,锁的对象是调用当前方法的对象;需要注意的是,静态方法以对象的字节码文件为(.class文件)作为锁对象。1、基本作用:Java提供了一种内置的锁机制来支持原子性,同步代码块(Synchronized Block )锁重入表示获取锁的粒度是线程。(1)编写一个同步方法。原创 2024-01-08 20:37:00 · 508 阅读 · 0 评论 -
多线程-生产者消费者模型
1、场景介绍:厨师和吃货的例子,吃货吃桌子上的面条,吃完让厨师做,厨师做完面条放桌子上,让吃货吃,厨师如果发现桌子上有面条,就不做,吃货发现桌子上没有面条就不吃。原创 2024-01-08 18:07:18 · 501 阅读 · 0 评论 -
如何让主线程等待子线程执行完毕后再执行
总结三个方法:(1)join方法,直接将每个线程都join进去即可。(2)ountdownlatch,内部维持一个计数操作,通过cas算法保证操作的原子性,当线程执行完之后,调用countDown()方法,将计数减一,主线程调用countdownlatch对象的await()方法,将当前线程挂起,直到countdownlatch对象的计数为0,主线程执行。(3)类似屏障的东西,当子线程执行完之后,调用CyclicBarrier对象的await()方法,直到指定数量的线程到达屏障之后,才会继续执行主线程。把每原创 2022-06-22 10:48:02 · 2534 阅读 · 0 评论 -
多线程—自旋优化
概念:当一个对象的锁已经被线程A获取,那么线程Bx再想获取该对象锁,就会失败,自旋锁就会一直循环获取,判断是否可以获取到锁,直到获取到锁才退出循环(while循环)。优点:自旋锁可以一直循环获取,那么获取锁的线程就会一直活跃,避免了线程上下文的切换。缺点:线程一直活跃,但是没有执行有效的任务,会造成busy-waiting。注意:自旋会占用cpu时间,单核cpu自旋不行,多核cpu才能发挥优势。另外, Java 6 之后自旋是自适应的,比如:刚刚操作成功一次,那么就认为自旋成功的可能性原创 2021-11-01 14:11:29 · 530 阅读 · 0 评论 -
多线程—线程优先级并不是绝对,只是概率更大。
优先级并不是绝对,只是概率更大。使用yield()方法让出该线程的时间片和设置线程的优先级,只会提示调度器,但是仅仅是一个提示,调度器是可以忽略它的。比如:当cup比较忙,那么优先级高的线程会获得更多的时间片,但是cpu闲暇时,优先级基本没用。优先级高,只代表获取时间片的机会更大,不能保证一定会获取到时间片。...原创 2021-10-29 15:14:09 · 163 阅读 · 0 评论 -
多线程—线程池
1.线程池状态状态名 高3位 接受新任务 处理阻塞队列任务 说明 RUNNING 111 Y Y SHOUTDOWN 000 N Y 不会接受新的任务,但是会处理阻塞队列的剩余任务 STOP 001 N N 会中断正在执行的任务,并且抛弃阻塞队列任务 TIDYING 010 任务全执行完毕,活动线程为0即将进入终结 TERMINATED 011原创 2021-10-28 17:54:27 · 147 阅读 · 1 评论 -
多线程—final
在Java中final关键字的作用:1、修饰属性:属性有一次赋值的机会,一旦属性被赋值,那么这个值将无法被修改。属性加了final之后,会产生一个写屏障:写屏障之前的代码不会指令重排序到写屏障之后。 写屏障之前的变量修改之后,都会同步到主存中。2、修饰方法:该方法不能被重写。3、修饰类:该类不能被继承。表示该类是不可变类,例如:熟悉的String类就是。...原创 2021-10-26 10:57:14 · 637 阅读 · 0 评论 -
多线程—原子引用
AtomicReference AtomicMarkableReference AtomicStampedReference 三个类的区别: AtomicReference:将引用类型变量封装起来,实现原子操作,但是无法识别ABA问题。 AtomicStampedReference:可以识别ABA问题,通过版本号识别,每次更改共享变量之后版本号都会加1。此时做CAS操作时,不仅仅是比较当前值和期望值是否相等,还要比较版本号是否相等,如果不相等则不是进行CAS操作。..原创 2021-10-25 15:31:46 · 92 阅读 · 0 评论 -
多线程—原子整数
AtomicInteger类相关原子方法。构造方法://有参数构造方法public AtomicInteger(int initialValue) { value = initialValue; }//无参构造方法public AtomicInteger() { }自增方法://自增public final int getAndIncrement() { //先取值再自增 return unsafe.getAndAddInt(this, valueOf原创 2021-10-25 14:56:04 · 126 阅读 · 0 评论 -
多线程—CAS
概念:CAS(Compare And Swap)是对一种处理器指令的称呼。先看一段代码:private static int count = 0;public static void add(){ count++;}如果多个线程调用add()方法,会有线程安全问题: 第一,count静态变量的可见性问题。 第二,count++操作的原型性问题。 那么如何解决呢?第一种方案:将count++放在synchronized同步代码块里,原子性和可见性都可以解决,但原创 2021-10-25 13:48:18 · 1377 阅读 · 0 评论 -
多线程—happens-before原则
概念:规定了对共享变量的写操作对其它线程的读操作可见。常见情况总结: 线程对object解锁之前对变量的写操作,对于接下来对object加锁其它线程对变量的读可见。 示例代码理解: 如果t1执行在t2之前,那么t1对变量的写操作,对于t2的读操作是可见的。 static int x;static Object object = new Object();new Thread(()->{ synchronized(object){ x = 1原创 2021-10-23 14:14:35 · 84 阅读 · 0 评论 -
多线程—Java内存模型
Java内存模型,JMM(Java Memory Model)。概念:Java内存模型定义了final、volatile和synchronized关键字的行为并确保正确同步的Java程序能够正确运行在不同架构的处理器上。作用:主要解决三个方面的问题 原子性问题 可见性问题 有序性问题 分析: 原子性:保证指令不会受到线程上下文切换的影响。 理解: 【原子】:字面意思是不可分割的意思,如果一个线程访问共享变量的操作,在其它线程看来是不可分割的,那么该操作就是原创 2021-10-22 17:54:00 · 439 阅读 · 2 评论 -
多线程—顺序控制
场景:两个线程的输出顺序需要固定。synchronized实现://使得输出顺序是t1-->t2的固定顺序 打印:首先-->然后@Slf4jpublic class ThreadSo { //创建锁的对象 static final Object object = new Object(); //创建标记 static Boolean mark = false; public static void main(String[] args)原创 2021-10-21 18:01:36 · 130 阅读 · 0 评论 -
多线程—ReentrantLock
你可能觉得很陌生,那我就把它和synchronized比较一下!与synchronized区别:不同点: 可中断,运行代码块时,可以中断。 可以设置超时时间,例如:等待一段时间之后,还没有获取到锁,那么就放弃等待,去执行其他的任务代码。 可以设置为公平锁,可以防止线程饥饿。 支持多个条件变量,相当于有多个WaitSet等待队列,根据等待条件的不同而区分。 相同点: 都支持可重入 基本用法://创建一个ReentrantLock对象Reentr原创 2021-10-21 17:59:22 · 620 阅读 · 0 评论 -
多线程—活锁
概念:活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束。示例代码:@Slf4jpublic class LockTset { //静态共享变量 private static int count = 10; public static void main(String[] args) { //一直减 new Thread(()->{ while(count > 0){原创 2021-10-21 17:45:13 · 177 阅读 · 0 评论 -
多线程—死锁
1、死锁出现场景:当线程A拥有了A对象的锁,想要去获取B对象的锁;线程B拥有了B对象的锁,想要拥有A对象的锁,两个线程在获取锁的时候,都不会释放已经持有的锁,于是,就造成了死锁。示例代码:@Slf4jpublic class ThreadTest { private static Object objectA = new Object(); private static Object objectB = new Object(); public static void原创 2021-10-21 09:34:38 · 333 阅读 · 0 评论 -
多线程—线程状态转换
如图,单箭头代表不可逆转,双箭头代表可以互相转换。序号1、NEW->RUNNABLE当调用线程的start()方法时,由NEW——>RUNNABLE。序号2、RUNNABLE<——>WAITING 线程使用synchronized(object)获取对象之后。 Object调用wait()方法时,线程从RUNNABLE——>WAITING Object调用notify(),notifyAll(),interrupt()时 如果线程...原创 2021-10-21 09:25:29 · 186 阅读 · 0 评论 -
多线程—wait/notify
重难点:理解object和线程之间的关系以及wait、notify、notifyAll的调用者和调用时刻。原理:相关方法: 首先需要明确是:下面三个方法都是属于,synchronized锁的那个对象的,因此不管是在哪里出现这几个方法,都应该是那个对象来调用这三个方法,对象是调用者,线程不是。 其次就是Monitor和对象之间的关系,java对象的对象头里面的Mark Work指向Monitor(Java环境看不到,属于操作系统),每个对象关联一个Monitor。 ob.原创 2021-10-21 09:21:47 · 85 阅读 · 0 评论 -
多线程—偏向锁
简单了解一下Mark Word(64bits)的结构:如图,上面一个没有开启偏向锁,下面一个是偏向锁,它们的区别主要是在biased_lock的值不一样,0和1的区别。另外,一个记录的是hashcode,一个记录的是线程id。这里有个小小知识点:当偏向锁是立即启动的情况下,提前访问了对象的hashcode码(如上图红箭头),那么会禁用掉偏向锁。为什么呢?原因:访问对象的时会生成hashcode码,存入了mark word ,而如果是偏向锁状态,那么将没有空间存储hashcode...原创 2021-10-20 18:15:00 · 399 阅读 · 0 评论 -
多线程—锁消除
概念:当一个对象的锁已经被线程A获取,那么线程Bx再想获取该对象锁,就会失败,自旋锁就会一直循环获取,判断是否可以获取到锁,直到获取到锁才退出循环。优点:自旋锁可以一直循环获取,那么获取锁的线程就会一直活跃,避免了线程上下文的切换。缺点:线程一直活跃,但是没有执行有效的任务,会造成busy-waiting。注意:自旋会占用cpu时间,单核cpu自旋不行,多核cpu才能发挥优势。另外, Java 6 之后自旋是自适应的,比如:刚刚操作成功一次,那么就认为自旋成功的可能性会更高,就会多自旋原创 2021-10-21 09:22:41 · 89 阅读 · 0 评论 -
多线程—锁膨胀
当线程0已经获取了Objec的轻量级锁,当线程1再获取时,就获取不到了,cas就会失败,此时就进入了锁膨胀过程。(1)object对象会申请Monitor重量级锁,让Object记录重量级锁的地址。(2)申请完之后,线程0时重量级锁(Monitor)的owner。(3)线程1进入重量级锁(Monitor)的EntryList 阻塞(BLOCKED)。当线程0的同步代码块执行完了之后,需要解锁时,需要按照重量级锁的解锁流程解锁了。找到Monitor,设置Owner为null...原创 2021-10-20 15:45:00 · 103 阅读 · 0 评论 -
多线程—轻量级锁
使用场景:如果一个对象被多个线程使用,但是每个线程使用对象的时间是错开的,也就是多个线程之间没有竞争,那么,可以使用轻量级锁来优化。语法:使用synchronized场景:假设两个方法同步块,对同一个对象加锁。private static final Object object = new Object();public static void methodOne(){ synchronized(onject){ //同步代码块 methodTwo;原创 2021-10-20 15:30:00 · 187 阅读 · 0 评论 -
多线程—Monitor(监听器)
Java对象的对象头里面的MarkWork指向Monitor(Java环境看不到,属于操作系统),每个对象关联一个Monitor。 线程2获取到了对象锁,那么线程2就是该Monitor的Owner。 当线程2没有释放锁,线程1获取对象锁时,线程1就会进入到EntryList阻塞队列里面,同时线程1的状态变为BLOCKED阻塞状态。 当锁释放了之后,其它在EntryList队列里面的线程就会竞争。 当线程获取了锁,但是需要达成某些条件才能继续执行,那么可以...原创 2021-10-20 11:03:32 · 925 阅读 · 0 评论 -
多线程—线程安全问题
首先看一段简单的代码:/*** 两个线程同时操作一个共享变量,可能会出现线程安全问题*/@Slf4jpublic class Thread1 { private static int current; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i原创 2021-10-20 10:06:36 · 223 阅读 · 0 评论 -
多线程—线程状态
线程状态大家可以分为两种:1、从操作系统层面。a.初始状态:在代码中线程刚创建出来的状态。b.可运行状态:线程处于等待状态,等待cpu分配时间碎片,然后运行线程。c.运行状态:线程正在运行的状态。d.阻塞状态:比如线程在进行IO等文件读写的时候,就会让出cpu,等待IO执行完毕之后,线程恢复到可运行状态,等待cpu分配时间碎片。e.终止状态:线程运行结束的状态。注意:cpu只会给可运行状态的线程分配时间碎片,不会给阻塞状态的线程分配时间碎片。2、从Java层面。(1)NE原创 2021-10-20 09:57:21 · 186 阅读 · 0 评论 -
多线程—守护线程
在Java中有两类线程,用户线程(User Thread)、守护线程(daemon Thread),守护线程是所有非守护线程的保姆。 只要JVM中有一个非守护线程没有结束,守护线程就全部工作;只有当非守护线程全部结束,守护线程才随着JVM一起结束工作。 常见的守护线程: (1) 垃圾回收器线程是一种守护线程。 (2)Tomcat中的Acceptor和Poller线程都是守护线程,所以Tomcat接收到shutdowm命令后,不会等待他们处理完当前请求。...原创 2021-10-20 09:51:39 · 537 阅读 · 0 评论 -
多线程—interrupt()方法
1、首先大家应该知道sleep()、wait()、join()调用者三个方法可以使得线程阻塞,线程阻塞之后,cpu是不会分配时间片的。2、interrupt()方法:作用:打断线程。1、打断处于阻塞状态的线程。2、打断正在运行的线程。注意:1、当使用interrupt()方法去打断处于阻塞状态的线程时,会以异常的方式打印,而不会更新打断标记,因此,虽然被打断,但是打断标记依然为false。2、当使用interrupt()方法去打断正在运行线程时,被...原创 2021-10-20 09:44:11 · 3578 阅读 · 1 评论 -
多线程—join()方法
join()方法场景:两个线程并发执行,如果A线程需要用到了B线程的返回结果,那么,就需要使用join方法,等到B线程运行完毕,返回了结果之后,A线程才继续执行。用法:场景中需要获取B线程的返回结果,那么就调用B线程的join方法。参数:join可以传参数,表示时间,单位为毫秒,意为最多等待多久时间之后,等待结束。以场景为例:如果线程A还没达到等待时间,但是线程B已经结束,那么将结束等待。如果线程A超过等待时间将不再等待,线程A将继续运行join下面的代码。原理:join()方法常用的原创 2021-10-20 09:33:10 · 1756 阅读 · 0 评论 -
多线程—sleep和yield深入理解
多线程—sleep和yieldThread.sleep(时间)方法作用:让线程休眠,让出该线程占用的cpu,线程从Running状态变为TimedWaiting状态(阻塞),传入的参数是线程休眠的时间,单位是毫秒。用法:在哪个线程里面调用,就让哪个线程休眠。注意:睡眠结束后的线程未必会立刻得到执行。另外:其他线程使用interrupt方法(下次讲)打断正在休眠的线程,这是sleep方法会抛出InterruptedExceptionjdk1.5之后,提供了TimeUnit.SECONDS.sl原创 2021-10-19 18:03:38 · 855 阅读 · 0 评论 -
多线程—进程与线程
进程与线程(1)进程程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理IO的。当一个程序被运行,从磁盘加载这个程序的代码至内存,就开启了一个进程。(2)线程一个进程之内可以有一到多个线程。一个线程就是一个指令流,将指令流中的一条条指令按一定的顺序交给CPU。Java中,线程作为最小的调度单位,进程作为资源分配的最小单位。(3)二者对比进程基本上相互独立,而线程存在于进程内,原创 2021-10-19 17:55:46 · 75 阅读 · 0 评论