JUC(2)

文章基于【狂神说Java】JUC并发编程最新版通俗易懂_哔哩哔哩_bilibili 视频总结而来

目录

7.集合类不安全

ArrayList不安全的解决办法:

HashSet不安全的解决办法:

HashMap不安全的解决方法:

8.Callable

和runnable的区别:

9. JUC 三大常用辅助类

CountDownLatch

CyclicBarrier

10. 读写锁

ReadWriteLock

11. 阻塞队列

四组API:

SynchronousQueue:

12. 线程池

好处:

创建线程三大方法:

创建单个线程的线程池:

 创建一个固定的线程池:

使用可伸缩线程池:

线程池七大参数:

七大参数

拒绝策略:

最大线程如何定义:


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的区别:

  1. 可以有返回值

  2. 可以抛出异常

  3. 方法不同

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. 阻塞队列

如果队列满了,写入会阻塞

如果队列空了,读取会阻塞

什么情况下会使用阻塞队列:

  1. 多线程并发处理

  2. 线程池

四组API:

  1. 抛出异常

  2. 不抛出异常

  3. 超时等待

  4. 阻塞等待

方式抛出异常不抛出异常,有返回值阻塞等待超时等待
添加addoffer(返回false)putoffer
移除removepoll(返回null)takepoll
判断队列首elementpeek(返回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. 线程池

池化技术:

程序的运行,本质就是占用系统的资源,优化资源的使用

线程池,连接池,内存池,对象池

池化技术: 事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我

好处:

  1. 降低资源的消耗

  2. 提高响应的速度

  3. 方便管理

线程复用,控制最大并发数,管理线程

创建线程三大方法:

创建单个线程的线程池:

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() // 尝试获取最早的线程竞争,不抛出异常

最大线程如何定义:

  1. CPU密集型 几核心就是几,可以保证CPU的效率最高

  2. IO密集型 判断程序中十分耗IO的线程总数,两倍

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PigeonEssence

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值