文章基于【狂神说Java】JUC并发编程最新版通俗易懂_哔哩哔哩_bilibili 视频总结而来
目录
7.集合类不安全
ArrayList不安全的解决办法:
我们的list接口在单线程的环境下是十分安全的:
package com.demo.unsafe;
import java.util.Arrays;
import java.util.List;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ListTest.java
* @Description TODO
* @createTime 2022年04月28日 15:20:00
*/
public class ListTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("1", "2", "3");
list.forEach(System.out::println);
}
}
但是在多线程的状态下就会出问题:
package com.demo.unsafe;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ListTest.java
* @Description TODO
* @createTime 2022年04月28日 15:20:00
*/
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i <10 ; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
java.util.ConcurrentModificationException // 并发修改异常
这个错误会出现在线程并发的时候,所以在并发环境下,ArrayList是不安全的。
解决方案1:
我们可以使用vector解决:
package com.demo.unsafe;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ListTest.java
* @Description TODO
* @createTime 2022年04月28日 15:20:00
*/
public class ListTest {
public static void main(String[] args) {
List<String> list = new Vector<>();
for (int i = 0; i <10 ; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
解决方案2:
使用Collection工具类让ArrayList变得安全
package com.demo.unsafe;
import java.util.*;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ListTest.java
* @Description TODO
* @createTime 2022年04月28日 15:20:00
*/
public class ListTest {
public static void main(String[] args) {
List<String> list = Collections.synchronizedList(new ArrayList<>());
for (int i = 0; i <10 ; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
解决方案3:
使用 JUC 的 CopyOnWriteArrayList:
package com.demo.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ListTest.java
* @Description TODO
* @createTime 2022年04月28日 15:20:00
*/
public class ListTest {
public static void main(String[] args) {
// 写入时复制 COW,提高效率,多个线程调用的时候,读取时固定,写入的时候可能会有覆盖操作,在写入的时候避免覆盖,造成数据问题。
// 读写分离: 写入的时候复制一个数组出来,写完的时候再插入进去,保证最终一致性,线程安全
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i <10 ; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
CopyOnWriteArrayList 相比 Victor优势:
CopyOnWriteArrayList 相比 Victor没有使用synchronize锁,而是用的lock锁+COW,性能会更好。
CopyOnWrite的添加方法:
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
victor的添加方法:
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
HashSet不安全的解决办法:
多线程环境下同样会出现 ConcurrentModificationException
package com.demo.unsafe;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName SetTest.java
* @Description TODO
* @createTime 2022年04月28日 16:27:00
*/
public class SetTest {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
for (int i = 0; i <30 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
解决方案1:
使用Collections转换同步set
package com.demo.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName SetTest.java
* @Description TODO
* @createTime 2022年04月28日 16:27:00
*/
public class SetTest {
public static void main(String[] args) {
Set<String> set = Collections.synchronizedSet(new HashSet<>());
for (int i = 0; i <30 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
解决方案2:
使用CopyOnWriteSet
package com.demo.unsafe;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName SetTest.java
* @Description TODO
* @createTime 2022年04月28日 16:27:00
*/
public class SetTest {
public static void main(String[] args) {
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i <30 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
HashSet的底层:
就是hashmap
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
添加方法就是加个值,本质就是 map, key无法重复
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
HashMap不安全的解决方法:
HashMap的底层有两个关键词:
loadFactor 加载因子
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
initialCapacity 初始容量
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and the default load factor (0.75).
*
* @param initialCapacity the initial capacity.
* @throws IllegalArgumentException if the initial capacity is negative.
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
初始容量的定义:
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/**
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<30.
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
DEFAULT_INITIAL_CAPACITY是用了位运算
也就是说,对于一个新的HashMap对象:
Map<String,String> map = new HashMap<>();
//等同于
Map<String,String> map = new HashMap<>(16,0.75f);
多线程环境下还是会出现线程不安全的错误 ConcurrentModificationException :
package com.demo.unsafe;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MapTest.java
* @Description TODO
* @createTime 2022年04月28日 16:40:00
*/
public class MapTest {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>(16,0.75f);
for (int i = 0; i <30 ; i++) {
new Thread(()->{
String s = UUID.randomUUID().toString().substring(0, 5);
map.put(s, s);
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
解决方案1:
使用collection同步Map:
package com.demo.unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MapTest.java
* @Description TODO
* @createTime 2022年04月28日 16:40:00
*/
public class MapTest {
public static void main(String[] args) {
Map<String,String> map = Collections.synchronizedMap(new HashMap<>());
for (int i = 0; i <30 ; i++) {
new Thread(()->{
String s = UUID.randomUUID().toString().substring(0, 5);
map.put(s, s);
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
解决方案2:
使用ConcurrentHashMap
package com.demo.unsafe;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName MapTest.java
* @Description TODO
* @createTime 2022年04月28日 16:40:00
*/
public class MapTest {
public static void main(String[] args) {
Map<String,String> map = new ConcurrentHashMap<>();
for (int i = 0; i <30 ; i++) {
new Thread(()->{
String s = UUID.randomUUID().toString().substring(0, 5);
map.put(s, s);
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
对于ConcurrentHashMap为什么可以解决线程安全问题,我们来看看底层:
对与put的方法:
/**
* Maps the specified key to the specified value in this table.
* Neither the key nor the value can be null.
*
* <p>The value can be retrieved by calling the {@code get} method
* with a key that is equal to the original key.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}
* @throws NullPointerException if the specified key or value is null
*/
public V put(K key, V value) {
return putVal(key, value, false);
}
/** Implementation for put and putIfAbsent */
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) {
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
我们可以看到,在put代码中,ConcurrentHashMap 维护了一个 Node <K,V>[] tab,这样一个Node 对象的数组,我们看看这个Node对象的源码:
/**
* Key-value entry. This class is never exported out as a
* user-mutable Map.Entry (i.e., one supporting setValue; see
* MapEntry below), but can be used for read-only traversals used
* in bulk tasks. Subclasses of Node with a negative hash field
* are special, and contain null keys and values (but are never
* exported). Otherwise, keys and vals are never null.
*/
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
Node(int hash, K key, V val, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.val = val;
this.next = next;
}
public final K getKey() { return key; }
public final V getValue() { return val; }
public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
public final String toString(){ return key + "=" + val; }
public final V setValue(V value) {
throw new UnsupportedOperationException();
}
public final boolean equals(Object o) {
Object k, v, u; Map.Entry<?,?> e;
return ((o instanceof Map.Entry) &&
(k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
(v = e.getValue()) != null &&
(k == key || k.equals(key)) &&
(v == (u = val) || v.equals(u)));
}
/**
* Virtualized support for map.get(); overridden in subclasses.
*/
Node<K,V> find(int h, Object k) {
Node<K,V> e = this;
if (k != null) {
do {
K ek;
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
} while ((e = e.next) != null);
}
return null;
}
}
可以看到 volatile Node<K,V> next 是被 volatile 关键字修饰的,具有原子可见性。
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
在这里可以看到,针对Object对象进行了CAS操作
8.Callable
和runnable的区别:
-
可以有返回值
-
可以抛出异常
-
方法不同
package com.demo.unsafe;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName CallTest.java
* @Description TODO
* @createTime 2022年04月29日 11:18:00
*/
public class CallTest {
public static void main(String[] args) {
new Thread().start();
MyThread thread = new MyThread();
FutureTask<String> futureTask = new FutureTask<String>(thread);
new Thread(futureTask,"A").start();
// 结果会被缓存
new Thread(futureTask,"B").start();
try {
// get方法可能会产生阻塞,放到最后处理
String o = futureTask.get();
System.out.println(o);
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("call");
return "11111";
}
}
9. JUC 三大常用辅助类
CountDownLatch
允许一个或者多个线程等待,知道在其他县城中执行的一组操作完成的同步辅助
就是一个减法计数器
package com.demo.unsafe;
import java.util.concurrent.CountDownLatch;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName CutDownDemo.java
* @Description TODO
* @createTime 2022年04月29日 14:04:00
*/
public class CountDownDemo {
public static void main(String[] args) {
//总数是6
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <=6 ; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
try {
// 等待计数器归零,再向下执行
countDownLatch.await();
System.out.println("END");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
数量-1
countDownLatch.countDown();
等待计数器归零,再向下执行
countDownLatch.await();
每次有现成调用,CountDown 数量 -1,假设计数器变成0,countdownLunch.await就会被唤醒,继续执行
CyclicBarrier
共同屏障
package com.demo.unsafe;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName CyclicBarrierDemo.java
* @Description TODO
* @createTime 2022年04月29日 14:13:00
*/
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, new Thread(()->{
System.out.println("召唤成功");
}));
for (int i = 1; i <=7 ; i++) {
final int temp = i;
new Thread(()->{
System.out.println("召唤 "+temp);
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
throw new RuntimeException(e);
}
},String.valueOf(i)).start();
}
}
}
Semaphore
信号量,维持一组许可证。
package com.demo.unsafe;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName SemaphoreDemo.java
* @Description TODO
* @createTime 2022年04月29日 14:30:00
*/
public class SemaphoreDemo {
public static void main(String[] args) {
//线程数量,是否公平, 限流
Semaphore semaphore = new Semaphore(3,true);
for (int i = 0; i <6 ; i++) {
new Thread(()->{
//得到
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"启动");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+"关闭");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
获得,如果已经满了,那就等待释放
semaphore.acquire();
释放,信号量释放+1,唤醒等待的线程
semaphore.release();
10. 读写锁
ReadWriteLock
可以被多线程同时读,只能有一个线程写
package com.demo.unsafe;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName ReadWriteLockDEmo.java
* @Description 读写锁
* @createTime 2022年04月29日 14:39:00
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 0; i < 5; i++) {
int finalI = i;
new Thread(()->{
myCache.put(String.valueOf(finalI),finalI);
}).start();
}
for (int i = 0; i < 5; i++) {
int finalI = i;
new Thread(()->{
myCache.get(String.valueOf(finalI));
}).start();
}
}
}
/**
* 模拟内存,可读写
*/
class MyCache{
private final Map<String,Object> map = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
/**
* 插入模拟内存方法
* @param key k
* @param value v
*/
public void put(String key, Object value){
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入");
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入完成");
}catch (Exception e){
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
/**
* 读取模拟内存方法
* @param key k
*/
public void get(String key){
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取");
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完成: "+o);
}catch (Exception e){
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
}
读 - 读 可以共存
读 - 写 不能共存
写 - 写 不能共存
11. 阻塞队列
如果队列满了,写入会阻塞
如果队列空了,读取会阻塞
什么情况下会使用阻塞队列:
-
多线程并发处理
-
线程池
四组API:
-
抛出异常
-
不抛出异常
-
超时等待
-
阻塞等待
方式 | 抛出异常 | 不抛出异常,有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer(返回false) | put | offer |
移除 | remove | poll(返回null) | take | poll |
判断队列首 | element | peek(返回null) |
package com.demo.unsafe;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName BlockingQueueDemo.java
* @Description 阻塞队列
* @createTime 2022年04月29日 15:31:00
*/
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
test5();
}
public static void test1(){
// 队列大小
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
// System.out.println(arrayBlockingQueue.add("d"));
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
//抛出异常
// System.out.println(arrayBlockingQueue.remove());
}
public static void test2(){
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
// 返回布尔值,false
System.out.println(arrayBlockingQueue.offer("d"));
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
// 会返回 null
System.out.println(arrayBlockingQueue.poll());
}
public static void test3(){
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
System.out.println(arrayBlockingQueue.element());
System.out.println(arrayBlockingQueue.peek());
}
public static void test4() throws InterruptedException {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.put("a");
arrayBlockingQueue.put("b");
arrayBlockingQueue.put("c");
//队列没有位置
arrayBlockingQueue.put("d");
//一直阻塞
arrayBlockingQueue.take();
arrayBlockingQueue.take();
arrayBlockingQueue.take();
//一直阻塞
arrayBlockingQueue.take();
}
public static void test5() throws InterruptedException {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.offer("a",2, TimeUnit.SECONDS);
arrayBlockingQueue.offer("b",2, TimeUnit.SECONDS);
arrayBlockingQueue.offer("c",2, TimeUnit.SECONDS);
arrayBlockingQueue.offer("d",2, TimeUnit.SECONDS);
System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));
System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));
System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));
System.out.println(arrayBlockingQueue.poll(2,TimeUnit.SECONDS));
}
}
SynchronousQueue:
同步队列,没有容量,必须等待驱逐来后,才能再往里面放一个元素
put,take
package com.demo.unsafe;
import java.util.concurrent.SynchronousQueue;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName SynchronizeQueueDemo.java
* @Description 同步队列
* @createTime 2022年04月29日 15:55:00
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
SynchronousQueue<String> objects = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" -》put 1");
objects.put("1");
System.out.println(Thread.currentThread().getName()+" -》put 2");
objects.put("2");
System.out.println(Thread.currentThread().getName()+" -》put 3");
objects.put("3");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},"T1").start();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" -》take 1");
objects.take();
System.out.println(Thread.currentThread().getName()+" -》take 2");
objects.take();
System.out.println(Thread.currentThread().getName()+" -》take 3");
objects.take();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},"T2").start();
}
}
12. 线程池
池化技术:
程序的运行,本质就是占用系统的资源,优化资源的使用
线程池,连接池,内存池,对象池
池化技术: 事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我
好处:
-
降低资源的消耗
-
提高响应的速度
-
方便管理
线程复用,控制最大并发数,管理线程
创建线程三大方法:
创建单个线程的线程池:
package com.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName PoolDemo1.java
* @Description TODO
* @createTime 2022年04月29日 17:25:00
*/
public class PoolDemo1 {
public static void main(String[] args) {
//单个线程
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i <10 ; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了");
});
}
executorService.shutdown();
}
}
可以看到,所有的任务由一个线程,线程1执行
package com.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName PoolDemo1.java
* @Description TODO
* @createTime 2022年04月29日 17:25:00
*/
public class PoolDemo1 {
public static void main(String[] args) {
//单个线程
//ExecutorService executorService = Executors.newSingleThreadExecutor();
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i <10 ; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了");
});
}
executorService.shutdown();
}
}
创建一个固定的线程池:
package com.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName PoolDemo1.java
* @Description TODO
* @createTime 2022年04月29日 17:25:00
*/
public class PoolDemo1 {
public static void main(String[] args) {
//单个线程
//ExecutorService executorService = Executors.newSingleThreadExecutor();
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i <10 ; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了");
});
}
executorService.shutdown();
}
}
可以看到,五个线程协同完成了任务
使用可伸缩线程池:
package com.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Zeyu Wan
* @version 1.0.0
* @ClassName PoolDemo1.java
* @Description TODO
* @createTime 2022年04月29日 17:25:00
*/
public class PoolDemo1 {
public static void main(String[] args) {
//可伸缩的
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i <10 ; i++) {
executorService.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了");
});
}
executorService.shutdown();
}
}
线程池七大参数:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
本质是调用了ThreadPoolExecutor()
ThreadPoolExecutor中有七个参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
七大参数
corePoolSize // 核心线程池大小
maximumPoolSize // 最大核心线程大小
keepAliveTime // 存活时间 超时了没有人调用就会释放
unit // 单位
workQueue // 阻塞队列
threadFactory //创建线程的工厂,一般不懂
handler //拒绝策略
拒绝策略:
AbortPolicy() // 满了就不处理,并抛出异常
CallerRunsPolicy() // 哪儿来的去哪儿
DiscardPolicy() // 满了就不处理,不会抛出异常
DiscardOldestPolicy() // 尝试获取最早的线程竞争,不抛出异常
最大线程如何定义:
-
CPU密集型 几核心就是几,可以保证CPU的效率最高
-
IO密集型 判断程序中十分耗IO的线程总数,两倍