跳至主要內容

JUC

Alooc...大约 6 分钟编程语言JavaJava

1. 概念

  • JUC: java.utl.concrrent
  • 进程和线程
  • wait和sleep
  • 并发和并行
  • 管程
    • Monitor监视器,也称锁,是一种同步机制,保证同一时间,只有一个线程访问被保护数据或代码
    • JVM同步资源的进入和退出,是用管程对象实现的
  • 用户线程和守护线程:
    • 自定义线程,
    • 后台的线程,比如垃圾回收
    • 主线程结束,自定义线程仍在,如果没有用户线程,只有守护线程,JVM结束

2. 两种实现多线程的方法

  • synchronized关键字

    • wait()
    • notifyAll()
  • lock实现类

    • await()
    • signalAll()

3. 多线程编程的步骤

  1. 创建资源类,在资源类创建属性和操作方法
  2. 在资源类操作方法:
    1. 判断
    2. 干活
    3. 通知
  3. 创建多个线程,调用资源类的操作方法
  4. 防止虚假唤醒,判断使用while
    • wait是在哪里睡了,就在哪里醒来
    • 虚假唤醒:一个线程在唤醒等待的线程后,有一部分满足条件,有一部分不满足条件(唤醒同类型判断的其他线程),不满足条件的被唤醒的线程属于虚假唤醒,解决方法是使用while循环判断是不是满足条件,这样不满足条件的线程就会再次等待。

4. 线程键的定制通信

普通的线程通信,只是把通知启动

定制通信,修改标志位,做到定制通知启动

  • flag=1 线程A Condition1
  • flag=2 线程B Condition2
  • flag=3 线程C Condition3

5. 集合的线程安全

  1. ArrayList线程不安全,解决方法:

    • 使用vector

    • Collections.synchronizedList(new ArrayList<>())

    • CopyOnWriteArrayList 并发读,独立写。写时复制

  2. HashSet线程不安全,解决方法:

    • CopyOnWriteHashSet
  3. HashMap线程不安全,解决方法:

    • ConcurrentHashMap

6. 多线程锁

synchronized八种类型的锁:

	## 公平锁 ,非公平锁

默认是非公平锁:会造成进一个线程活动,造成线程饿死现象,但效率高

公平锁:阳光普照,但效率相对低

可重入锁

可重入锁:外层钥匙可打开内部所有层的门

synchronoized 隐式可重入锁,隐式,自动完成上锁解锁

lock 显式可重入锁,手动声明上锁解锁

死锁

  1. 什么是死锁: 两个或两个以上的线程在执行过程中,因为争夺资源而造成一种互相等待的现象,如果没有外力支持,他们无法再执行下去。

  2. 产生死锁的原因:

    1. 系统资源不足
    2. 进程运行推进顺序不合适
    3. 资源分配不当
  3. 验证是否是死锁

    1. jps
    2. jstack jvm自带的堆栈跟踪工具

手写死锁示例:


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");
                synchronized (b) {
                    System.out.println(Thread.currentThread().getName() + " 获取锁b");
                }
            }
        },"A").start();

        new Thread(() -> {
            synchronized (b) {
                System.out.println(Thread.currentThread().getName() + " 持有锁b,试图获取锁a");
                synchronized (a) {
                    System.out.println(Thread.currentThread().getName() + " 获取锁a");

                }
            }
        },"B").start();


    }
}


7. Callable接口

创建线程的4种方式:

1. 继承Thread类
1. 实现Runnable接口
1. 实现Callable接口
1. 线程池

Runnale,线程终止时无法获取返回结果,因此出现了Callable接口

Callable,Runnable的区别:

  • 是否有返回值
  • 是否抛出异常
  • 实现方法不同,void run() V call()

使用calllable接口,runnable接口有实现类FutureTask,此类构造方法可以传递callable参数

new Thread(new FutureTask(new MyThread2()),"BB").start();


FutureTask<Integer> futureTask2 = new FutureTask<>(()->{
            System.out.println(Thread.currentThread().getName()+"  come in callable");
            return 1024;
        });

8. JUC强大的辅助类

1. CountDownLatch 减少计数

设置初始值,每次调用会减一,执行等待,当计数器值为0时运行

例子:6个同学离开教室后,才会锁门。

public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);

        // 6个同学陆续离开教师
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                countDownLatch.countDown();
                System.out.println(Thread.currentThread().getName() + "号同学离开了教室");
            },String.valueOf(i)).start();
        }
        countDownLatch.await();

        System.out.println(Thread.currentThread().getName() + "班长锁门");

    }

2. CyclicBarrier 循环栅栏

一个同步辅助类,它允许一组线程互相等待,直到达某个公共屏障点。

例子:集齐七颗龙珠就可以召唤神龙

	// 创建固定值
    private static final int NUM = 7;

    public static void main(String[] args) {
        // 创建CyclicBarrier
        CyclicBarrier cyclicBarrier =
                new CyclicBarrier(NUM,()->{
                    // 未达到7不会执行,一致等待,等达到7后执行下面语句
                    System.out.println("集群7颗龙珠就可以召唤神龙");
                });
        // 集齐7颗龙珠的过程
        for (int i = 1; i <= 7; i++) {
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName()+"星龙珠被收集到了");
                    // 等待
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (BrokenBarrierException e) {
                    throw new RuntimeException(e);
                }
            },String.valueOf(i)).start();
        }
    }

3. Semaphore 信号灯

一个计数信号量。信号量维护一个许可集

例子:6辆汽车,停3个车位

    //6辆汽车,停3个车位
    public static void main(String[] args) {
        // 创建Semaphore,设置许可数量
        Semaphore semaphore = new Semaphore(3);
        // 模拟6辆汽车
        for (int i = 1; i <= 6 ; i++) {
            new Thread(() -> {
                try {
                    //抢占
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 抢到了车位");
                    //设置随机停车时间
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                    System.out.println(Thread.currentThread().getName() + "-------离开了车位");

                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } finally {
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }

9. 读写锁 ReentrantReadWriteLock

  1. 悲观锁和乐观锁

  2. 表锁和行锁,行锁会发生死锁

  3. 读锁和写锁,读锁是共享锁,写锁是独占锁,两者都会发生死锁

  • 读写锁:一个资源可以被多个读线程访问,或者可以被一个写线程访问,但是不能同时存在读写线程,读写互斥,读读共享。

  • 读写锁的演变过程:

  • 锁降级:将写入锁降级为读锁

jdk8,先获取写锁,然后获取读锁,之后释放写锁,完成降级,最后释放读锁。

拥有写锁可以再次获取读锁,而拥有读锁,不能再获取写锁。

读锁不能升级为写锁。

10. 阻塞队列

分类:

方法:

11. 线程池ThreadPool

架构:

七个参数:

int corePoolSize,
int maximumPoolSize
long keepAliveTime
TimeUnit unit
BlockingQueue<Runnable> workQueue
ThreadFactory threadFactory
RejectedExecutionHandler handler
  • corePoolSize
    • 常驻线程数量(核心)
  • maximumPoolSize
    • 最大线程数量
  • keepAliveTime
    • 线程存活时间 值
  • unit
    • 线程存活时间 单位
  • workQueue
    • 阻塞队列
  • threadFactory
    • 线程工厂
  • handler
    • 拒绝策略,线程全部使用后的拒绝策略

底层工作流程:

自定义线程池:

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                2,
                5,
                2L,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());


        try {
            for (int i = 1; i <= 10; i++) {
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " 办理业务");
                });
            }
        } finally {
            threadPool.shutdown();
        }
    }

12. Fork/Join分支合并框架

class MyTask extends RecursiveTask<Integer> {
    private static final Integer  VALUE = 10;
    private int begin;
    private int end;
    private int result;

    public MyTask(int begin , int end){
        this.begin = begin;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        if ((end-begin) <= 10){
            for (int i = begin ; i < end ; i++) {
                result += i;
            }
        } else {
            int middle = (begin+end) >> 1;
            MyTask task1 = new MyTask(begin,middle);

            MyTask task2 = new MyTask(middle+1,end);

            task1.fork();
            task2.fork();

            result = task1.join() + task2.join();
        }

        return result;
    }
}
public class ForkJoinDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyTask task = new MyTask(0,100);
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(task);
        Integer res = forkJoinTask.get();
        System.out.println(res);
        forkJoinPool.shutdown();

    }
}

13. 异步回调 CompletableFuture

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 异步调用,没有返回值
        CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(()->{
            System.out.println(Thread.currentThread().getName()+"completableFuture1");
        });
        completableFuture1.get();

        // 异步调用,有返回值
        CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+"completableFuture1");
            return 1024;
        });
        completableFuture2.whenComplete((t,u)->{
            System.out.println("----t="+t);
            System.out.println("----u="+u);
        }).get();
    }
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.5