JUC 知识点

本文详细介绍了Java并发编程中JUC(Java Concurrency Utilities)的基础知识,包括线程、进程的概念,线程状态,wait和sleep的区别,以及并发与并行的定义。此外,还讲解了Lock接口、线程间通信、线程定制化通信、集合线程安全问题及其解决方案,如CopyOnWrite技术。文章进一步讨论了多线程锁的使用,包括synchronized与Lock的区别,以及不同场景下的锁策略。最后,作者探讨了线程池的实现、阻塞队列、读写锁以及异步回调等高级主题,旨在帮助读者深入理解Java并发编程。
摘要由CSDN通过智能技术生成

写在前面

  1. 本文在学习JUC过程中,自己总结的一份JUC知识点,涵盖知识点,代码Demo
  2. 源码 GITHUB 地址:源码地址
  3. 将会持续更新
  4. 如果有什么地方总结的不完善或出现错误,欢迎留言补充和改正,共同进步!

JUC 基础

1. 什么是线程和进程?举例说明

  • 进程是系统中正在运行的一个程序,是系统资源分配的独立实体,每个进程都拥有独立的地址空间,程序一旦运行就是进程。

  • 进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。


  • 线程通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。

  • 线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。


  • 举例:QQ / WeChat 就是一个进程,每一个聊天窗口就是一个个线程。


2. 线程的状态有哪些?

  • 线程有六种状态:新建,运行,阻塞,等待,定时等待,终结

  • java.lang.Thread.State 枚举类

  • public enum State {
         
     
           NEW,
     
           RUNNABLE,
     
           BLOCKED,
     
           WAITING,
     
           TIMED_WAITING,
     
           TERMINATED;
       }
    

3. wait 和 sleep 的区别?

  • wait() 是Object 包提供的方法,sleep() 是Thread包下提供的方法
  • java.lang.Thread#sleep()
  • java.lang.Object#wait()
  • wait 是放开锁睡眠,睡眠结束从新争取资源
  • sleep 线程不会释放对象锁睡眠,睡眠结束不需要争取资源,整个程序在其睡眠期间阻塞等待

4. 什么是并发,什么是并行?

  • 并发:同一时刻多个线程访问同一个资源,多个线程对一个点。如:秒杀
  • 并行:多项任务同时进行。(一边… 一边…,泡方便面,电水壶烧水,一边撕调料倒入桶中)

5. JUC 常见异常

  • java.util.ConcurrentModificationException:并发修改异常
  • java.util.NoSuchElementException:常发生在阻塞队列
  • java.util.concurrent.RejectedExecutionException:触发线程池默认拒绝策略,AbortPolicy

JUC Lock接口

1. Lock 和 Synchronized 区别在哪?

  • Lock是个接口,而 synchronized 是java关键字,synchronized 是内置语言实现
  • synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象的发生;而Lock在发生异常时,如果没有主动通过unlock()去释放锁,则很有可能造成死锁现象,因此使用Lock时需要在finally 块中释放锁。
  • 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到,不能判断锁状态
  • 在性能上来说,如果资源竞争不激烈的话,两者的性能是差不多的;而当资源竞争非常激烈(即有大量线程同时竞争)时,Lock的性能要远远优于synchronized
  • 少量同步可用 synchronized,大量同步时一般使用 Lock

2. 经典买票问题

  • synchronized

    public class SaleTicket {
         
    
        public static void main(String[] args) {
         
    
            Ticket ticket = new Ticket();
            int number = 2000;
    
            new Thread(() -> {
         
                for (int i = 0; i < number; i++) {
         
                    ticket.sale();
                }
            }, "A").start();
    
            new Thread(() -> {
         
                for (int i = 0; i < number; i++) {
         
                    ticket.sale();
                }
            }, "B").start();
    
            new Thread(() -> {
         
                for (int i = 0; i < number; i++) {
         
                    ticket.sale();
                }
            }, "C").start();
        }
    }
    
    
    class Ticket {
         
        private int number = 300;
    
        synchronized void sale() {
         
            if (number > 0) {
         
                System.out.println(Thread.currentThread().getName() 
                                   + "剩余:" + --number + "张");
            }
        }
    }
    

  • Lock

    public class SaleTicket02 {
         
        public static void main(String[] args) {
         
            Ticket02 ticket = new Ticket02();
    
            int number = 4000;
    
            new Thread(() -> {
         
                for (int i = 0; i < number; i++) {
         
                    ticket.sale();
                }
            }, "AA").start();
    
            new Thread(() -> {
         
                for (int i = 0; i < number; i++) {
         
                    ticket.sale();
                }
            }, "BB").start();
    
            new Thread(() -> {
         
                for (int i = 0; i < number; i++) {
         
                    ticket.sale();
                }
            }, "CC").start();
        }
    }
    
    
    class Ticket02 {
         
        private Lock lock = new ReentrantLock();
    
        private int number = 3000;
    
        void sale() {
         
            lock.lock();
            try {
         
                if (number > 0) {
         
                    System.out.println(Thread.currentThread().getName() 
                                       + "剩余:" + --number + "张");
                }
            } catch (Exception e) {
         
                e.printStackTrace();
            } finally {
         
                lock.unlock();
            }
        }
    }
    

JUC 线程间通信

1. 多线程的虚假唤醒

  • TODO 待完善

2. 面试题一:

问题: 现在两个线程。可以操作一个初始值为0的变量,实现一个线程对改变量加一,一个线程对该变量减一。实现交替执行10轮后,变量初始值为0。

  • synchronized 方式:

    class Operation {
         
        private int number = 0;
    
        synchronized void increment() throws InterruptedException {
         
            // 必须为while,防止线程虚假唤醒
            while (number != 0) {
         
                this.wait();
            }
            System.out.println(Thread.currentThread().getName() + ",当前数+1, 为: " + ++number);
            this.notifyAll();
        }
    
        synchronized void decrement() throws InterruptedException {
         
            while (number != 1) {
         
                this.wait();
            }
            System.out.println(Thread.currentThread().getName() + ",当前数-1, 为: " + --number);
            this.notifyAll();
        }
    }
    
    public class ThreadWaitNotifyDemo {
         
        public static void main(String[] args) {
         
            int num = 11;
            Operation operation = new Operation();
    
            new Thread(() -> {
         
                for (int i = 1; i < num; i++) {
         
                    try {
         
                        operation.increment();
                    } catch (InterruptedException e) {
         
                        e.printStackTrace();
                    }
                }
            }, "AA").start();
    
            new Thread(() -> {
         
                for (int i = 1; i < num; i++) {
         
                    try {
         
                        operation.decrement();
                    } catch (InterruptedException e) {
         
                        e.printStackTrace();
                    }
                }
            }, "BB").start();
        }
    }
    

  • Lock 方式

    public class ThreadWaitNotifyDemo02 {
         
        public static void main(String[] args) {
         
            int num = 10;
            Operation02 operation02 = new Operation02();
    
            new Thread(() -> {
         
                for (int i = 0; i < num; i++) {
         
                    operation02.increment();
                }
            }, "AA").start();
    
            new Thread(() -> {
         
                for (int i = 0; i < num; i++) {
         
                    operation02.decrement();
                }
            }, "BB").start();
        }
    }
    
    class Operation02 {
         
        private int number = 0;
    
        private ReentrantLock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        void increment() {
         
            lock.lock();
            try {
         
                while (number != 0) {
         
                    condition.await();
                }
                System.out.println(Thread.currentThread().getName() + ",\t" + ++number);
                condition.signalAll();
            } catch (Exception e) {
         
                e.printStackTrace();
            } finally {
         
                lock.unlock();
            }
        }
    
        void decrement() {
         
            lock.lock();
            try {
         
                while (number != 1) {
         
                    condition.await();
                }
                System.out.println(Thread.currentThread().getName() + ",\t" + --number);
                condition.signalAll();
            } catch (Exception e) {
         
                e.printStackTrace();
            } finally {
         
                lock.unlock();
            }
        }
    }
    

3. 面试题二:

两个线程,一个线程打印1-52,另一个打印字母A-Z打印顺序为12A34B…5152Z,要求用线程间通信。

public class ThreadWaitNotifyDemo03 {
   
    public static void main(String[] args) {
   
        Handler handler = new Handler();

        new Thread(handler::printNum, "打印1-52").start();

        new Thread(handler::printChar, "打印A-Z").start();

    }
}

class Handler {
   
    private static int num = 1;

    private Lock lock = new ReentrantLock();
    private Condition priNum = lock.newCondition();
    private Condition priChr = lock.newCondition();

    void printNum() {
   
        lock.lock();
        try {
   
            int total = 53;
            for (int i = 1; i < total; i++) {
   
                while (num 
### 回答1: Java JUCJava Util Concurrent)是Java平台的一个并发编程库,提供了一些并发编程的工具和框架。以下是Java JUC的一些重要知识点: 1. Lock接口和ReentrantLock类:提供了一种比Java中的synchronized关键字更灵活、可定制化的同步机制。 2. Condition接口:可以和Lock接口一起使用,提供了一种等待通知机制,可以让线程在等待某个条件成立时挂起,直到被其他线程唤醒。 3. Semaphore类:提供了一种信号量机制,可以限制某些资源的并发访问量,保证程序的稳定性。 4. CountDownLatch类:提供了一种倒计时锁机制,可以让某个线程在其他线程都完成后再执行。 5. CyclicBarrier类:提供了一种栅栏机制,可以让多个线程在某个点上进行同步,等待所有线程都到达后再同时执行。 6. Executor框架:提供了一种线程池机制,可以更好地管理线程,提高程序的性能和稳定性。 7. CompletableFuture类:提供了一种异步编程机制,可以让程序在等待某些操作的同时继续执行其他操作,提高程序的并发性能。 这些都是Java JUC的重要知识点,掌握它们可以帮助开发者更好地编写高并发、高性能的程序。 ### 回答2: Java JUCJava Util Concurrency)是Java并发编程的工具类库,提供了一些多线程编程的辅助工具和数据结构,主要包括锁、原子变量、并发容器、线程池等。 首先,Java JUC提供了多种类型的锁,如ReentrantLock、ReadWriteLock等。这些锁可以用来控制对共享资源的访问,保证线程的安全性。通过使用锁,可以实现线程的互斥访问和公平竞争访问,防止资源的并发访问导致的数据不一致的问题。 另外,Java JUC还提供了一些原子变量,比如AtomicInteger、AtomicLong等。原子变量是线程安全的,可以保证对其操作的原子性。通过使用原子变量,可以避免多线程环境下对共享变量的竞争导致的数据错乱问题。 并发容器也是Java JUC的重要组成部分,如ConcurrentHashMap、ConcurrentLinkedQueue等。这些并发容器是线程安全的,可以在多线程环境下安全地处理数据。通过使用并发容器,可以提高多线程程序的性能和并发访问的效率。 最后,Java JUC还提供了线程池的支持,通过线程池可以实现线程的复用、统一管理和调度。线程池可以减少线程的创建和销毁的开销,并且可以控制并发线程的数量,避免因为线程数过多导致系统资源耗尽的问题。 总之,Java JUC知识点涵盖了锁、原子变量、并发容器和线程池等多个方面,可以帮助程序员更好地进行多线程编程,提高程序的性能和并发访问的效率。 ### 回答3: Java JUCjava.util.concurrent)是Java中用于处理多线程并发编程的工具包。它提供了一套强大的并发编程工具和类,帮助开发者更加方便地编写高效、稳定的多线程程序。 Java JUC包含了以下几个重要的知识点: 1. 锁机制:Java JUC提供了多种类型的锁机制,包括ReentrantLock、StampedLock等,用于实现线程同步和互斥访问共享资源。通过使用锁机制,可以确保多个线程之间的数据一致性和线程安全性。 2. 阻塞队列Java JUC提供了多种类型的阻塞队列,如ArrayBlockingQueue、LinkedBlockingQueue等。阻塞队列是一种特殊的队列,当队列为空或者已满时,插入和删除操作会被阻塞,直到满足条件后再继续执行。 3. 线程池:Java JUC中的线程池机制可以重用线程,减少线程的创建和销毁开销,提高系统的性能和资源利用率。通过ThreadPoolExecutor类,可以方便地创建和管理线程池,并根据实际需求调整线程池的大小和线程池中线程的执行方式。 4. 原子操作:Java JUC提供了一系列原子类,如AtomicInteger、AtomicLong等,用于支持对共享变量进行原子操作,以避免线程竞争和数据不一致的问题。原子类提供了一系列原子性的方法,保证了多线程环境下的安全访问。 5. 并发容器:Java JUC提供了一些线程安全的并发容器,如ConcurrentHashMap、CopyOnWriteArrayList等,用于在多线程环境下安全地处理数据结构。这些并发容器支持高并发读写操作,提供更好的性能和可伸缩性。 总之,Java JUC提供了一组强大的并发编程工具和类,能够帮助开发者更好地处理多线程编程中的并发性和线程安全性问题。通过熟练掌握和应用这些知识点,可以编写出高效、稳定的多线程程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值