本文主要内容:读写锁、阻塞队列和自定义线程池
五、读写锁
ReentrantReadWriteLock
:读写锁出现就是为了更加方便的操作多条线程对资源的读写
readWriteLock.readLock()
:读锁 == 共享锁
readWriteLock.writeLock()
:写锁 == 独占锁
public class ReadWriteLockTest {
public static void main(String[] args) {
MyThreads myThreads = new MyThreads();
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(() -> {
myThreads.write(temp + "", temp);
}, String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(() -> myThreads.read(temp + ""), String.valueOf(i)).start();
}
}
}
class MyThreads{
private volatile Map<String, Object> map = new HashMap<>();
// 声明一个读写锁
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 写入的时候只有一个线程可以写,不希望被其它线程插入(就是不希望在A线程正在写入和写入成功成功之间有其它线程进入)
public void write(String key, Object value){
readWriteLock.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 {
readWriteLock.writeLock().unlock();
}
}
// 读取的时候多条线程可以同时读取
public void read(String key){
readWriteLock.readLock().lock();
try {
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取成功");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
六、阻塞队列
public class BlockingQueueTest {
/**
* 阻塞队列API一:add()与remove()方法
*/
@Test
public void test1(){
// 声明一个队列长度为3的阻塞队列
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
// 添加元素
System.out.println(queue.add("a"));
System.out.println(queue.add("b"));
System.out.println(queue.add("c"));
// 如果此时继续使用add方法会抛出异常(超过队列长度) java.lang.IllegalStateException: Queue full
// System.out.println(queue.add("d"));
// 获取元素
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
// 如果此时继续使用add方法会抛出异常(没有元素) java.util.NoSuchElementException
// System.out.println(queue.remove());
}
/**
* 阻塞队列API二:offer()与poll()方法
*/
@Test
public void test2(){
// 声明一个队列长度为3的阻塞队列
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
// 添加元素
System.out.println(queue.offer("a"));
System.out.println(queue.offer("b"));
System.out.println(queue.offer("c"));
// 如果此时继续使用offer方法不会抛出异常,只会返回false
System.out.println(queue.offer("d"));
// 获取元素
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
// 如果此时继续使用offer方法不会抛出异常,只会返回null
System.out.println(queue.poll());
}
/**
* 阻塞队列API三:put()与take()方法
*/
@Test
public void test3() throws InterruptedException {
// 声明一个队列长度为3的阻塞队列
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
// 添加元素
queue.put("a");
queue.put("b");
queue.put("c");
// 如果此时继续使用put方法不会抛出异常,只会一直阻塞
// queue.put("d");
// 获取元素
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println(queue.take());
//如果此时继续使用take方法不会抛出异常,只会一直阻塞
//System.out.println(queue.take());
}
/**
* 阻塞队列API四:offer()与poll()方法的带参版
*/
@Test
public void test4() throws InterruptedException {
// 声明一个队列长度为3的阻塞队列
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
// 添加元素
System.out.println(queue.offer("a"));
System.out.println(queue.offer("b"));
System.out.println(queue.offer("c"));
// 如果此时继续使用offer方法传递2s,则会在2s之后退出阻塞
System.out.println(queue.offer("d",2, TimeUnit.SECONDS));
// 获取元素
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
// 如果此时继续使用poll方法传递2s,则会在2s之后退出阻塞
System.out.println(queue.poll(2, TimeUnit.SECONDS));
}
}
四种情况分析 | 报出异常 | 不报异常 | 一直阻塞 | 超时退出 |
---|---|---|---|---|
添加方法 | add() | offer() | put() | offer(参数) |
删除方法 | remove() | poll() | take() | poll(参数) |
七、线程池
创建线程池的三大方法
public class ThreadPool {
public static void main(String[] args) {
// 创建只有一个线程的线程池
// ExecutorService executor = Executors.newSingleThreadExecutor();
// 创建有自定义个线程的线程池
// ExecutorService executor = Executors.newFixedThreadPool(5);
// 创建弹性线程池,根据需要的线程数量,自行创建
ExecutorService executor = Executors.newCachedThreadPool();
try {
for (int i = 1; i <= 5; i++) {
executor.execute(() -> {
System.out.println(Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭线程池
executor.shutdown();
}
}
}
自定义线程池所需的七大参数和四种拒绝策略(因为我们需要规避默认创建线程池的OOM问题)
public class ThreadPool {
public static void main(String[] args) {
// 创建自定义线程池
ExecutorService executor = new ThreadPoolExecutor(
2, // 池的核心大小
// 池的最大线程数:一般要根据需求确定是CPU密集型还是IO密集型
// CPU密集型:池的最大线程数为CPU核心数 Runtime.getRuntime().availableProcessors() --> 获取CPU最大核心数
// IO密集型:池的池的最大线程数一般为频繁IO操作任务的数量 * 2
Runtime.getRuntime().availableProcessors(),
2, TimeUnit.SECONDS, // 超时过期时间
new LinkedBlockingQueue<>(3), // 阻塞队列
Executors.defaultThreadFactory(), // 创建线程工厂
// 拒绝策略1.new ThreadPoolExecutor.AbortPolicy() 线程池已经耗尽,如果还需要获取线程就直接抛出异常
// 拒绝策略2.new ThreadPoolExecutor.CallerRunsPolicy() 线程池已经耗尽,如果还需要获取线程,就直接让其回到创建线程的线程去执行(主线程)
// 拒绝策略3.new ThreadPoolExecutor.DiscardPolicy() 线程池已经耗尽,如果还需要获取线程,就不去处理该方法即不抛出异常
// 拒绝策略4.new ThreadPoolExecutor.DiscardOldestPolicy() 线程池已经耗尽,如果还需要获取线程,就尝试和线程池最老线程争取资源
// 线程池的最大承载量为:池的最大线程数 + 阻塞队列可以容纳的容量
new ThreadPoolExecutor.AbortPolicy()
);
try {
for (int i = 1; i <= 8; i++) {
executor.execute(() -> {
System.out.println(Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭线程池
executor.shutdown();
}
}
}