JUC并发编程

1  JUC概述

JUC是java.util.concurrent工具包简称,用于处理线程的工具包,java1.5开始出现。

进程Process:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配的最小单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是操作系统结构的基础。

线程thread 是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

1.1 线程状态

新建状态NEW

准备就绪状态RUNNABLE

阻塞状态BLOCKED

等待状态WAITING

过时不候TIMED_WAITING

终结状态TERMINATED

1.2 wait和sleep区别

(1) sleep是Thread的静态方法,wait是Object的方法,任何对象实例都能调用。

(2) sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized 中)。

(3)它们都可以被interrupted方法中断。

1.3 并发和并行

串行模式:所有任务都按先后顺序执行,一次只能取得一个任务并执行

并行模式:可以同时取得多个任务,并同时执行这些任务。

并行:同一时刻多个线程访问同一资源,多个线程对一个点。(电商秒杀)

并发:多项工作一起执行,之后汇总。(泡方便面,烧水和倒调料同时进行)

1.4 管程

也叫Monitor监视器,是一种同步机制,保证同一时间只有一个线程访问被保护的数据或代码

jvm同步基于进入和退出,使用管程对象实现的,每个对象都会有个相应的管程对象,随着对象一起创建和销毁。管程对临界区加锁,进入时加锁,退出时解锁。

执行线程操作首先要持有管程对象,才能进行方法,当进行方法时,其他对象就不会获取同一个管程对象,当方法完成后最终释放管程对象。

1.5 用户线程和守护线程

用户线程:自定义线程

守护线程:后台自动运行线程,如垃圾回收线程

主线程结束,用户线程还在运行则jvm存活

主线程结束,无用户线程,全为守护线程则jvm结束

2 synchronized多线程编程

2.1 使用synchronized实现线程间通信

1.创建资源类,在资源类创建属性和方法

2.在资源类操作方法中定义条件变量为0,创建增减条件变量的synchronized方法,判断条件变量,增加方法中循环判断条件变量为0则+1,不为0则不+1并且wait,+1后通知其他线程;减小方法中循环判断条件变量为1则-1,否则不-1并且wait,-1后通知其他线程

3.创建资源类对象实例,创建多个线程对象,通过资源类对象调用资源类操作方法。new Thread(new Runnable(){实现run方法},线程名).start();

通过兰姆达表达式简化线程创建new Thread(()->{直接写run方法内容即可},线程名).start();

4.防止虚假唤醒问题

2.2 虚假唤醒

wait方法唤醒时会继续向下执行,如果使用if判断线程是否被占用,当被唤醒后不会再次判断,会继续下执行,导致虚假唤醒

3  Lock接口

3.1 Lock和Synchronized区别

Lock不是java语言内置的,Synchronized是java语言的关键字,是内置特性,通过该类可以实现同步访问

Synchronized不需要手动释放锁,会自动让线程释放对锁的占用;Lock必须要用户手动释放锁,不主动释放可能会出现死锁,所以需要在finally中释放锁

Lock可以让等待锁的线程响应中断,但synchronized不行,等待的线程会一直等待下去,无法响应中断

Lock可以知道是否成功获取了锁,但synchronized不行

Lock可以提高多个线程读的效率

3.2 使用Lock接口实现线程间通信

在资源类中创建private final ReentranLock lock = new ReentranLock()对象

private Condition condition = lock.newCondition();

资源类方法不需要加synchronized

condition对象可以当做不同的通道,线程可以在不同的通道内await,唤醒时可唤醒特定内通道进程

lock.lock();在执行前上锁

lock.unlock();在try-catch的finally中解锁

等待操作使用condition.await()代替wait

通知其他线程使用condition.signalAll()方法

3.3 线程间定制化通信

通过给每个线程赋予不同的标志位flag,在执行前判断flag是否符合自己的要求,轮到谁执行就让标志位为几,在执行完毕后将标志位修改为下一个执行的线程的标志位

通过在不同condition中await和signal实现定向唤醒

4 集合的线程安全

当使用线程不安全的集合时,可能导致数据读取错误,比如脏读等现象

4.1 ArrayList解决方法

1.使用Vector线程安全集合

2.使用Collections.synchronizedList(new ArrayList<>());

3.使用CopyOnWriteArrayList()或或

4.2 CopyOnWriteArrayList原理

在读取集合内容时,多个线程可以并发读取;在修改内容时,首先将集合复制一份,在复制体中修改写入,写入完成后与原来的集合合并,然后让线程都来新的集合读取

4.2.1源码

add方法首先创建锁对象,上锁,然后try中复制原有集合,获取长度,复制为一个长度为原来长度+1的集合,添加新元素进去,返回true,finally解锁

4.3 HashSet解决方案

CopyOnWriteArraySet()

4.4 HashMap解决方案

1.HashTable,底层使用Synchronized,全局锁效率较低

2.ConcurrentHashMap()

5 多线程锁

5.1 锁的作用范围

synchronized加在普通方法上锁的是对象,在对象解锁前其他的synchronized方法无法调用,但是其他的普通方法可以调用,并且也可以通过其他对象调用

synchronized加在静态方法上锁的是类,上锁后任何该类的实例对象都无法调用类中的其他静态同步方法,但是对普通synchronized方法不影响,仍然可以调用,相当于两把锁

synchronized加在同步方法快上,锁的是synchronized括号中的对象

5.2 公平锁和非公平锁

ReentrantLock无参构造器默认生成非公平锁对象

传入true参数则生成公平锁对象,传入false参数则生成非公平锁对象

非公平锁会造成饥饿,但是效率高;公平锁会使线程都能获取资源,但是效率低

非公平锁直接获取锁进行执行;公平锁传入acquires参数,通过hasQueuedPrecessors方法查看是否有人排队,无人则执行,有人则排队

5.3 可重入锁

synchronized和lock都是可重入锁,synchronized是隐式的,lock是显式的

可重入锁意思是在获取了锁后,可以多次递归调用自己(需要同一把锁的对象)

synchronized可以直接反复调用,lock则可以多次lock,然后多次unlock,如果少了unlock则后续线程无法获取锁,无法执行

5.4 死锁

产生原因:1.系统资源不足,2.进程运行推进顺序不合适,3.资源分配不当

检查死锁方法:1.命令行输入jps -l找到线程对应pid,2.命令行输入jstack pid值,可以看到found到的deadlock数量

6 辅助类

6.1 减少计数CountDownLatch

构造器中传入初始值

countDown方法每次对初始值-1

当初始值不为0时await等待变为0

当初始值为0时输出一段话 

计数器为0时被await阻塞的线程会被唤醒继续执行

当一个线程需要等待其他线程执行到某个位置才能继续执行,则在其他线程执行位置处加countDown方法,在线程中断等待处加await方法,计数器初始设置为等待线程数(提前新建计数器对象)

6.2 循环栅栏CyclicBarrier

允许一组线程相互等待,直到达到某个公共屏障点

创建时传入需要等待到达的线程数和Runnable接口实现类

每个线程到达指定位置调用CyclicBarrier.await方法等待

屏障释放后可以重用,所以称为循环栅栏

6.3 信号灯Semaphore

创建对象时设置许可数量,每次线程请求acquire则许可数-1,finally中release释放许可

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值