JUC基础知识理解

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:把分析任务的结果进行合并

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值