JUC并发编程
进程与线程
UC就是java.util.concurrent工具包 这是一个处理线程的工具包在JDK1.5进行使用的
进程
是计算机关于某数据集合上的一次运行活动 是系统资源分配和调度的基本单位
线程
是操作系统能够进行运算调度的最小单位是包含在进程之中的 是进程中实际运作的单位
用火车进行必须的话 进程就是整个火车 线程就是火车里面的每一个车厢
总结:
进程就是系统中正在运行的一个应用程序 程序一旦运行就是进程 进程是资源分配的额最小单位
线程是系统分配处理器时间资源的基本单位 或者说进程之内独立执行的一个单元执行流 线程-程序执行的最小单位
线程的状态
新建状态(NEW) :当用new操作符创建一个线程后此时线程处在新建状态。 当一个线程处于新建状态时,线程中的任务代码还没开始运行
就绪状态(Runnable):就绪状态 一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当调用了线程对象的start()方法即启动了线程,此时线程就处于就绪状态。处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他就绪线程竞争CPU,只有获得CPU使用权才可以进行运行
运行状态(Running):线程获取到CPU使用权进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。真正开始执行run()方法的内容
阻塞状态(Blocked):线程在获取锁失败时(因为锁被其它线程抢占),它会被加入锁的同步阻塞队列,然后线程进入阻塞状态(Blocked)。处于阻塞状态(Blocked)的线程放弃CPU使用权,暂时停止运行。待其它线程释放锁之后,阻塞状态(Blocked)的线程将在次参与锁的竞争,如果竞争锁成功,线程将进入就绪状态(Runnable)
等待状态(WAITING):或者叫条件等待状态,当线程的运行条件不满足时,通过锁的条件等待机制(调用锁对象的wait()或显示锁条件对象的await()方法)让线程进入等待状态(WAITING)。处于等待状态的线程将不会被cpu执行,除非线程的运行条件得到满足后,其可被其他线程唤醒,进入阻塞状态(Blocked)。调用不带超时的Thread.join()方法也会进入等待状态
超时等待(TIMED_WAITING):限时等待是等待状态的一种特例,线程在等待时我们将设定等待超时时间,如超过了我们设定的等待时间,等待线程将自动唤醒进入阻塞状态(Blocked)或就绪状态(Runnable) 。在调用Thread.sleep()方法,带有超时设定的Object.wait()方法,带有超时设定的Thread.join()方法等,线程会进入限时等待状态(TIMED_WAITING)
死亡等待:线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
wait和sleep
wait:是Object的方法 回释放锁 但调用他的前提是当前的线程占有锁
sleep:是thread的静态方法 不会释放锁 也不需要进行占用锁
共同点:都会被interrupted方法中断
并发与并行
并发:同一时刻多个线程在访问同一个资源 多个线程对一个点 举例: 秒杀
并行:过个任务一起执行 最后在进行汇总
管城
Monitor 监视器就是平时所说的锁 是一种同步机制 保证同一时间只有一个线程能够访问被保护数据或者代码
用户线程与守护线程
用户线程:平时所用的线程基本都是用户线程
守护线程:后台运行的线程 例如jvm的垃圾回收
public class Main {
public static void main(String[] args) {
// 如果是守护线程true 用户线程false
Thread thread1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "::" + Thread.currentThread().isDaemon());
while (true) {
}
}, "thread1");
//thread1.setDaemon(true); 设置成守护线程之后jvm就会结束了以为没有用户线程了
thread1.start();
System.out.println(Thread.currentThread().getName()+"over");
}
}
结果:main over thread1::false
主线程结束 用户线程还在继续 jvm存活
没有用户线程 都是守护线程 jvm结束
Lock接口
synchronized
是一种同步锁 修饰代码块的话被修饰的代码块被称为同步语句块 修饰方法的话被修饰的方法被称为同步方法
多线程编程步骤
1、创建资源类 在资源类创建属性和操作方法
2、创建多个线程 调用资源类的操作方法
测试
// 创建一个资源类 定义属性与方法
class Ticket{
private int number=30;
// 操作方法
public synchronized void sale(){
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出"+number--+"还剩:"+number);
}
}
}
public class SaleTicket {
public static void main(String[] args) {
Ticket ticket = new Ticket();
// 创建三个线程
new Thread(new Runnable() {
@Override
public void run() {
// 调用方法
for (int i=0;i<30;i++){
ticket.sale();
}
}
},"a").start();
new Thread(new Runnable() {
@Override
public void run() {
// 调用方法
for (int i=0;i<30;i++){
ticket.sale();
}
}
},"b").start();
new Thread(new Runnable() {
@Override
public void run() {
// 调用方法
for (int i=0;i<30;i++){
ticket.sale();
}
}
},"c").start();
}
}
abc 三个线程就会实现抢占线程 synchronized 可以实现自动释放锁
Lock锁实现提供了比使用同步方法和语句可以获得更广泛的锁的操作 实现了比synchronized更多的功能
Lock需要手动释放锁 synchronized可以实现自动释放锁
可重入锁
class LTicket{
// 票的数量
private int number=30;
// 创建可重入锁
private final ReentrantLock reentrantLock=new ReentrantLock();
//买票方法
public void sale(){
reentrantLock.lock();
try {
if (number>0){
System.out.println(Thread.currentThread().getName()+number--+"剩余"+number);
}
}finally {
reentrantLock.unlock();
}
}
}
public class LSaleTicket {
public static void main(String[] args) {
LTicket lTicket = new LTicket();
new Thread(()->{
for (int i=0;i<30;i++){
lTicket.sale();
}
},"AA").start();
new Thread(()->{
for (int i=0;i<30;i++){
lTicket.sale();
}
},"BB").start();
new Thread(()->{
for (int i=0;i<30;i++){
lTicket.sale();
}
},"CC").start();
}
}
调用了start也不会立即创建 需要系统分配 因为里面有一个start0()这个方法
线程间的通信
两个线程交替执行
public class ThreadDemo {
int num=0;
public synchronized void add() throws InterruptedException {
while (num!=0){
// 使用while来防止虚假唤醒
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName()+num);
this.notifyAll();
}
public synchronized void delete()throws InterruptedException{
while (num==0){
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName()+num);
this.notifyAll();
}
}
class Main{
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(()->{
for (int i=0;i<10;i++){
try {
threadDemo.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"a").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
threadDemo.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"c").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
threadDemo.delete();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"d").start();
}
}
使用Lock
class Share{
int num=0;
private Lock lock1=new ReentrantLock();
private Condition condition=lock1.newCondition();
public void add(){
lock1.lock();
try {
while (num!=0){
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName()+num);
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock1.unlock();
}
}
public void delete() throws InterruptedException {
lock1.lock();
try {
while (num==0){
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName()+num);
condition.signalAll();
}finally {
lock1.unlock();
}
}
}
public class LockThreadDemo {
public static void main(String[] args) {
}
}
线程的定制通信
让线程按照顺序执行
class ShareResource {
// 定义标志位 1AA 2BB 3CC
private int flag = 1;
private Lock lock = new ReentrantLock();
// 创建三个
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void print5(long loop) {
lock.lock();
try {
// 判断
while (flag != 1) {
condition1.await();
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
flag=2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10(long loop) {
lock.lock();
try {
// 判断
while (flag != 2) {
condition1.await();
}
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
flag=3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15(long loop) {
lock.lock();
try {
// 判断
while (flag != 3) {
condition1.await();
}
for (int i = 0; i < 15; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
flag=1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
ShareResource resource = new ShareResource();
new Thread(()->{
for (int i=0;i<10;i++){
resource.print5(i);
}
},"AA").start();
new Thread(()->{
for (int i=0;i<10;i++){
resource.print10(i);
}
},"BB").start();
new Thread(()->{
for (int i=0;i<10;i++){
resource.print15(i);
}
},"CC").start();
}
}
集合的线程安全
public class TestDemo4 {
public static void main(String[] args) {
// List<String> list = new ArrayList<>(); 存在ConcurrentModificationException异常
// List<String> list = new Vector<>(); 很慢不建议
// List<String> list= Collections.synchronizedList(new ArrayList<>());也不好
List<String> list = new CopyOnWriteArrayList<>();//内部用了lock锁
for (int i=0;i<50;i++){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
synchronized实现同步的基础:
Java中的每一个对象都可以作为锁 具体表现为以下三种形式
对于普通方法锁的是实例对象
对于静态同步方法锁的是当前类的Class对象
对于同步方法快锁的是括号里面的对象
多线程锁
公平锁与非公平锁
非公平锁
:线程饿死的情况 执行的效率高
公平锁
:线程都可以执行到 但是效率低
private final ReentrantLock lock=new ReentrantLock(true);
true:就是公平锁 false就是非公平锁
可重入锁
synchronized(隐士 自动释放锁)与Lock(显示 手动释放锁)
被锁锁住的里面可以共用这一个锁
死锁
两个以上的线程 在执行过程中因为争夺资源而造成的一种互相等待的现象 如果没有外力干涉 就无法在执行下去
原因:
1、系统资源不足
2、进程运行推进顺序不合适
3、资源分配不当
public class DeadLock {
static Object a=new Object();
static Object b=new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (a){
System.out.println(Thread.currentThread().getName()+"有a 尝试获取b");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){
System.out.println(Thread.currentThread().getName()+"a 获取了b");
}
}
},"a").start();
new Thread(()->{
synchronized (b){
System.out.println(Thread.currentThread().getName()+"有b 尝试获取a");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (a){
System.out.println(Thread.currentThread().getName()+"b 获取了a");
}
}
},"b").start();
}
}
3、验证是否是死锁
- jps 类似于Linux的 ps -ef 查看信息 jps -l
- jstack 查看堆栈信息 jvm自带的 jstack 查询出的线程号
通过Callable接口
Runnable与Callable接口的比较
- 是否有返回值
- 是否抛出异常
- 实现方法名称不同 一个是run方法 一个是call方法
class MyThread1 implements Runnable{
@Override
public void run() {
System.out.println("run");
}
}
class MyThread2 implements Callable{
@Override
public Integer call() throws Exception {
return 200;
}
}
public class Demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
new Thread(new MyThread1(),"AA").start();
//Callable接口
FutureTask<Integer> futureTask=new FutureTask<>(new MyThread2());
new Thread(futureTask,"bb").start();
System.out.println(futureTask.get());
}
}
CountDownLatch接口
- CountDownLatch主要有两个方法 当一个或多个线程调用await方法时 这些线程会进行阻塞
- 其他线程调用countDown方法会将计数器减1 (调用countDown方法的线程不会进行阻塞)
- 当计数器的值变为0 因await方法阻塞的此案城会被唤醒 继续执行
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch downLatch = new CountDownLatch(6);
for (int i=1;i<=6;i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" 号同学离开了教师");
// -1
downLatch.countDown();
},String.valueOf(i)).start();
}
// 在这个地方会进行阻塞 值为0的时候
downLatch.await();
System.out.println(Thread.currentThread().getName()+"班长锁门了");
}
}
**CyclicBarrier **循环栅栏
public class CyclicBarrierDemo {
private static final Integer NUMBER=7;
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(NUMBER, () -> {
System.out.println("神龙");
});
for (int i=1;i<=7;i++){
new Thread(()->{
try {
//等待
System.out.println(Thread.currentThread().getName()+"进行收集");
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
读写锁
读锁: 共享锁 会发生死锁
A线程修改时候需要等待B线程读之后才可以进行
B线程修改的时候需要等待A线程读取之后才可以 就会产生死锁
写锁: 独占锁 会发生死锁
A线程在操作第一条记录但是又要操作第二条
B在操作第二条但是又要操作第一条 就会产生死锁
class MyCache {
// 创建map集合
private volatile Map<String, Object> map = new HashMap<>();
private ReentrantReadWriteLock rwLock=new ReentrantReadWriteLock();
// 放数据
public void put(String key, Object value) throws InterruptedException {
rwLock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " 正在写操作" + key);
TimeUnit.MICROSECONDS.sleep(300);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + " 写完了" + key);
rwLock.writeLock().unlock();
}
// 取数据
public Object get(String key) throws InterruptedException {
rwLock.readLock().lock();
System.out.println(Thread.currentThread().getName() + " 正在去数据" + key);
TimeUnit.MICROSECONDS.sleep(300);
Object result ;
result = map.get(key);
System.out.println(Thread.currentThread().getName()+" 获取了数据"+result);
rwLock.readLock().unlock();
return result;
}
}
public class ReadWriteLock {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i=1;i<=5;i++){
int num=i;
new Thread(()->{
try {
myCache.put(num+"",num+"");
} catch (InterruptedException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
for (int i=1;i<=5;i++){
int num=i;
new Thread(()->{
try {
myCache.get(num+"");
} catch (InterruptedException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
读写锁:一个资源可以被多个读线程访问,或者可以被一个写线程访问 但是不能同时存在读写线程 读写互斥 读读共享
锁降级:将写入锁降级为读锁
jdk8说明:获取写锁 ->获取读锁->释放写锁-> 释放读锁
public static void main(String[] args) {
// 可重入读写锁对象
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
// 读锁
ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
// 写锁
ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
writeLock.lock();
System.out.println("write");
readLock.lock();
System.out.println("--read");
writeLock.unlock();
readLock.unlock();
}
读锁不能升级为写锁
public static void main(String[] args) {
// 可重入读写锁对象
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
// 读锁
ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
// 写锁
ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
readLock.lock();
System.out.println("--read");
writeLock.lock();
System.out.println("write");
readLock.unlock();
writeLock.unlock();
}
只会执行read 不会执行write 除非释放读锁才可以
阻塞队列
当队列是空的话 获取元素会进行阻塞
当队列满的时候 添加元素会阻塞
方法类型 | 抛出异常 | 特殊值 | 阻塞 | 超时 |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除 | remove() | poll() | take() | poll(time,unit) |
查找 | element() | peek() | 不可用 | 不可用 |
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
//创建阻塞队列
BlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
// blockingQueue.add("a");
// blockingQueue.add("b");
// blockingQueue.add("c");
// // 第四个会出现异常
blockingQueue.add("d");
// System.out.println(blockingQueue);
// blockingQueue.offer("a");
// blockingQueue.offer("b");
// blockingQueue.offer("c");
// // 第四个插不进去
// blockingQueue.offer("d");
// System.out.println(blockingQueue);
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
// 会进行阻塞
blockingQueue.put("d");
System.out.println(blockingQueue);
}
}
小Demo
通过线程池创建接口
七大参数
- int corePoolSize 核心线程数量
- int maximumPoolSize 最大线程数量
- long keepAliveTime 保持存活时间
- TimeUnit unit 单位
- BlockingQueue workQueue 阻塞队列
- ThreadFactory threadFactory 线程工厂
- RejectedExecutionHandler handler 拒绝策略
拒绝策略:
AbortPolicy(默认):直接抛出异常
CallerRunsPolicy:“调用者运行”一种调节机制 该策略不会抛弃任务 也不会抛出异常 而是将某些任务回退到调用者 从而降低新任务的流量
DiscardOlderPolicy:抛弃队列中等待很久的任务 然后吧当前任务加入到队列里面 尝试再次提交当前任务
DiscardPolicy: 直接丢弃不能处理的任务
分支合并框架
Fork:将一个复杂的任务进行分析
Join:把分析任务的结果进行合并