Are you sure you want to delete this task? Once this task is deleted, it cannot be recovered.
WuTianJuan 4fe0273cda | 1 year ago | |
---|---|---|
CategoryController.java | 1 year ago | |
CategoryServiceImpl.java | 1 year ago | |
DishService.java | 1 year ago | |
README.md | 1 year ago |
存放单一元素的集合
由Collection 接⼝派生:
存放键值对的集合
由Map接口派生
ArrayList 底层使⽤ Object存储,适⽤于频繁的查找⼯作,线程不安全 。
Vector 底层使⽤ Object存储,线程安全的。
newCachedThreadPool : 可缓存的线程池。
适用于并发执行大量短期的小任务。可能会创建大量线程,从而导致 OOM。
newFixedThreadPool : 创建固定数目线程的线程池。
适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,
尽可能的少的分配线程,即适用执行长期的任务。但是它不会拒绝任务,
在任务比较多的时候会导致 OOM。
newScheduledThreadPool : 支持定时及周期性的任务执行的线程池。
适合周期性执行任务的场景,需要限制线程数量的场景。
newSingleThreadPool : 单线程化的线程池。
适用于串行执行任务的场景,一个任务一个任务地执行。
在任务比较多的时候也是会导致OOM。
Future
的 get()
方法来获取返回值继承Thread类
实现Runnable接口
实现Callable接口
线程池
避免java单继承的限制
线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类
start()
。start()
等待运行的状态。![1667884358112](C:\Users\Zhang Songbo\AppData\Roaming\Typora\typora-user-images\1667884358112.png)
概念:两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象。若无外
力作用,它们都将无法推进下去。
四个条件:
避免死锁:
volatile
关键字可以保证变量的可见性,如果我们将变量声明为 volatile,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。
作用:
保证可见性:一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
防止指令重排序:通过对变量进行读写操作时,插入内存屏障来实现。
插入一个内存屏障,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命
令的必须后执行。对一个volatile字段进行写操作,Java内存模型将在写操作后插入一个写屏障指令,这个指令会把之前的写入值都刷新到内存。
注:不能保证原子性
修饰普通方法:作用于当前对象实例
修饰静态方法:作用于当前类
修饰代码块:指定加锁对象,对给定对象加锁
修饰代码块:通过 monitorenter 和 monitorexit 指令实现同步。
monitorenter 指令指向同步代码块的开始位置,
monitorexit 指令指向同步代码块的结束位置。
当执行monitorenter 指令时,线程试图获取锁也就是获取 monitor 的持有权,
其内部包含一个计数器,当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。
执行 monitorexit 指令后,将锁计数器设为0 ,表明锁被释放。
如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。
修饰方法:用ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法, JVM 通过该访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。
它默认采用偏向锁,然后再程序运行中始终是有一个线程去获取Synchronized的锁,java对象会记录线程的id,所以下次再获取Synchronized的锁的时候,只需要比较这个线程id就行。在运行过程中如果出现第二个线程去请求Synchronized锁的时候分两种情况。
在没有发生并发竞争锁的情况下,Synchronized就会自动升级为轻量级锁,这个时候第二个线程就会尝试自旋锁的方式来获取锁,因为很快就能拿到锁,所以第二个线程也不会阻塞。
出现两个线程竞争锁的情况,Synchronized就会升级为重量级锁,这个时候只有一个线程获取锁,另外一个线程会阻塞,等待第一个线程释放锁之后才能拿到锁。
可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全的,可以实现公平锁。
线程本地变量。当使用 ThreadLocal 维护变量时, ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程。
每一个Thread线程都会拥有自己的一个成员变量ThreadLocalMap,该变量默认为空(实际是ThreadLocal的静态内部类,只是Thread持有引用)。当往ThreadLocal存数据时,调用的是ThreadLocal的set方法,该方法先拿到当前线程的ThreadLocalMap,然后以当前threadLocal实例为key把数据存进该map中。当取数据时,一样拿到当前线程的ThreadLocalMap,并以当前threadLocal实例为key从里面拿出数据。
简而言之,存进ThreadLocal的数据,相当于存进了线程自己的变量中。因此如果要保证共享变量的隔离性,就把他放进ThreadLocal(相当于在线程中拷贝了一份),要用时,从ThreadLocal中拿出来即可(拿出当前线程的副本)
每个线程都有⼀个 ThreadLocalMap 的内部属性,map的key是 ThreaLocal ,定义为弱引用,value是强引用类型。
垃圾回收的时候会⾃动回收key,而value的回收取决于Thread对象的生命周期。一般会通过线程池的方式复用线程节省资源,这也就导致了线程对象的生命周期比较长,这样便一直存在一条强
引用链的关系: Thread --> ThreadLocalMap --> Entry --> Value ,随着任务的执行,value就有可能越来越多且无法释放,最终导致内存泄漏。
解决⽅法:每次使⽤完 ThreadLocal 就调⽤它的 remove() ⽅法,手动将对应的键值对删除,从⽽避免
内存泄漏。
AQS,抽象队列同步器,定义了一套多线程访问共享资源的同步器框架,像平常用的 ReentrantLock/Semaphore/CountDownLatch都是依赖于它的 。
AQS使用一个 volatile 的int类型的成员变量 state 来表示同步状态,通过CAS修改同步状态的值。
当线程调用 lock 方法时 ,如果 state =0,说明没有任何线程占有共享资源的锁,可以获得锁并将state 加1。
如果 state 不为0,则说明有线程目前正在使用共享变量,其他线程必须加入同步队列进行等待。
CAS全称 Compare And Swap ,比较与交换,是乐观锁的主要实现方式。CAS在不使用锁的情况下实现多线程之间的变量同步。 ReentrantLock 内部的AQS和原子类内部都使用了CAS。
CAS算法涉及到三个操作数:
需要读写的内存值V。
进行比较的值A。
要写入的新值B。
只有当V的值等于A时,才会使用原子方式用新值B来更新V的值,否则会继续重试直到成功更新值。
以 AtomicInteger 为例, AtomicInteger 的 getAndIncrement() 方法底层就是CAS实现,关键代码是
compareAndSwapInt(obj, offset, expect, update) ,其含义就是,如果 obj 内的 value 和
expect 相等,就证明没有其他线程改变过这个变量,那么就更新它为 update ,如果不相等,那就会继
续重试直到成功更新值。
公平锁与非公平锁:按照线程访问顺序获取对象锁。 synchronized 是非公平锁, Lock 默认是非公平锁,可以设置为公平锁,公平锁会影响性能。
共享式与独占式锁:他们俩个的区别在于同一时刻独占式只能有一个线程获取同步状态,而共享式在同一时刻可以有多个线程获取同步状态。例如读操作可以有多个线程同时进行,而写操作同一时刻只能有一个线程进行写操作,其他操作都会被阻塞。
悲观锁与乐观锁:悲观锁,每次访问资源都会加锁,执行完同步代码释放锁, synchronized 和 ReentrantLock 属于悲观
锁。 乐观锁,不会锁定资源,所有的线程都能访问并修改同一个资源,如果没有冲突就修改成功并退出,否则就会继续循环尝试。乐观锁最常见的实现就是 CAS 。
悲观锁适合写操作多的场景。 乐观锁适合读操作多的场景,不加锁可以提升读操作的性能。
相同点:
不同点:
1.ReentrantLock:可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全的。
2.Semaphore:表示 “可用资源的个数”,用于控制同时访问特定资源的线程数量,控制并发线程数。假如是5,程序执行前用acquire()方法获得信号,则可用信号变为4,程序执行完通过release()方法归还信号量,可用信号又变为5。如果可用信号为0,acquire就会造成阻塞,等待release释放信号。
3.CountDownLatch:用于某个线程等待其他线程执行完任务再执行,与thread.join()功能类似。常见的应用场景是开启多个线程同时执行某个任务,等到所有任务执行完再执行特定操作,如汇总统计结果。
4.CyclicBarrier:同步屏障,用于一组线程互相等待到某个状态,然后这组线程再同时执行。
CyclicBarrier和CountDownLatch区别:
5.CopyOnWriteArrayList:相当于线程安全的ArrayList,使用了一种叫写时复制的方法,当有新元素add到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。
CopyOnWriteArrayList 中add方法添加的时候是需要加锁的,保证同步,避免了多线程写的时候复制出多个副本。读的时候不需要加锁,如果读的时候有其他线程正在向 CopyOnWriteArrayList 添加数据,还是可以读到旧的数据。
CopyOnWrite并发容器用于读多写少的并发场景。
是否保证线程安全:
ArrayList 和 LinkedList 都不保证线程安全
底层数据结构:
ArrayList 底层使⽤的是 Object 数组
LinkedList 底层使⽤的是 双向链表
是否⽀持随机访问:
ArrayList 支持
LinkedList 不支持
Dear OpenI User
Thank you for your continuous support to the Openl Qizhi Community AI Collaboration Platform. In order to protect your usage rights and ensure network security, we updated the Openl Qizhi Community AI Collaboration Platform Usage Agreement in January 2024. The updated agreement specifies that users are prohibited from using intranet penetration tools. After you click "Agree and continue", you can continue to use our services. Thank you for your cooperation and understanding.
For more agreement content, please refer to the《Openl Qizhi Community AI Collaboration Platform Usage Agreement》