JUC学习
作者:互联网
JUC学习
1、线程的状态
public enum State {
// 新建
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待(一直等)
WAITING,
// 等待(超时等待)
TIMED_WAITING,
// 终止
TERMINATED;
}
辅助类
CountDownLatch(减法计数器)
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName());
// 数量减一
countDownLatch.countDown();
},String.valueOf(i)).start();
}
// 等待10个线程执行结束再往下执行(计数器为0时)
countDownLatch.await();
System.out.println("所有线程执行完毕!");
}
CyclicBarrier(加法计数器)
public static void main(String[] args) throws InterruptedException {
/**
* 集齐7颗龙珠,召唤神龙
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> System.out.println("召唤神龙!"));
for (int i = 1; i <= 7; i++) {
final int j = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "集齐第" + j + "颗龙珠");
try {
// 等待
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
Semaphore(信号量)
public static void main(String[] args) throws InterruptedException {
// 指定线程数:停车位
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
// 得到
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到车位");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
应用场景:限流
读写锁:ReentrantReadWriteLock
public static void main(String[] args) throws InterruptedException {
MyCache myCache = new MyCache();
// 5个线程写
for (int i = 1; i <= 5; i++) {
final int j = i;
new Thread(() -> myCache.put(String.valueOf(j), j),String.valueOf(i)).start();
}
// 5个线程读
for (int i = 1; i <= 5; i++) {
final int j = i;
new Thread(() -> myCache.get(String.valueOf(j)), String.valueOf(i)).start();
}
}
class MyCache {
private Map<String,Object> map = new HashMap<>();
// 读写锁
private ReadWriteLock lock = new ReentrantReadWriteLock();
// 写的时候,我们只希望一个线程写
public void put(String key, Object value) {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入OK");
} finally {
lock.writeLock().unlock();
}
}
// 读的时候我们希望多个线程读
public void get(String key) {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object value = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取OK");
} finally {
lock.readLock().unlock();
}
}
}
阻塞队列(BlockingQueue)
四组API
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | offer() |
移除 | remove() | poll() | take() | poll() |
检测队首元素 | element() | peek() | - | - |
线程池
三大方法
public static void main(String[] args) {
// 单个线程
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 固定数量线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 颗伸缩的,遇强则强,与弱则弱
// ExecutorService threadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + ":hello world");
});
}
threadPool.shutdown();
}
七大参数
// 通过Executors创建线程池的本质
public ThreadPoolExecutor(int corePoolSize, // 核心线程数大小
int maximumPoolSize, // 最大线程数大小
long keepAliveTime, // 存活时间,超过这个时间未调用就释放
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory, // 线程工厂,一般不用动
RejectedExecutionHandler handler) // 拒绝策略
四种拒绝策略
new ThreadPoolExecutor.AbortPolicy(); // 线程数超出 (最大线程数 + 阻塞队列长度) 则抛出异常
new ThreadPoolExecutor.CallerRunsPolicy(); // 哪来的去哪里 不会抛出异常
new ThreadPoolExecutor.DiscardPolicy(); // 队列满了,丢掉任务 不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy(); // 队列满了,尝试和第一个竞争,失败再丢掉任务 不会抛出异常
总结
不推荐使用Executors创建线程,而是使用ThreadPoolExecutor创建线程。
Executors是不安全的,有可能会把资源耗尽的风险
Executors弊端如下:
-
Executors.newFixedThreadPool() 和 Executors.newSingleThreadExecutor()
-
允许请求队列长度为Integer.MAX_VALUE(约21亿),可能会堆积大量的请求,从而导致OOM
-
-
Executors.newCachedThreadPool() 和 Executors.newScheduledThreadPool()
-
允许创建线程数量为Integer.MAX_VALUE(约21亿),可能会创建大量线程,从而导致OOM
-
最大线程数到底该如何定义
-
CPU密集型,几核就定义几,可以保持CPU的效率最高
// 通过java代码获取CPU核心数
System.out.println(Runtime.getRuntime().availableProcessors()); -
IO 密集型
一般设置要大于程序中比较耗IO资源的线程
Volatile关键字
volatile是java虚拟机提供的轻量级的同步机制
-
保证可见性
-
不保证原子性
-
禁止指令重排
什么是JMM?
java内存模型,不存在的东西,是一种概念也是一种约定
关于JMM的一些同步约定:
-
线程解锁前,必须把共享变量==立刻==刷回主存
-
线程加锁前,必须读取主存中最新值到工作内存中
-
加锁和解锁必须是同一把锁
保证可见性
// 不加volatile,程序就会进入死循环
private volatile static int a;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while (a == 0) { // 会进入死循环,此线程对主内存的变化是不知道的
}
}).start();
TimeUnit.SECONDS.sleep(1);
a = 1;
System.out.println("a = " + a);
}
不保证原子性
private volatile static int a;
public static void add() {
a ++;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println("a = " + a);
}
自定义自旋锁
public class SpinLock {
private static AtomicReference<Integer> atomicReference = new AtomicReference<>(1);
public void lock() {
while (!atomicReference.compareAndSet(1, 2)) {
}
System.out.println(Thread.currentThread().getName() + "加锁成功");
}
public void unlock() {
atomicReference.compareAndSet(2, 1);
System.out.println(Thread.currentThread().getName() + "解锁");
}
}
标签:JUC,Thread,System,学习,线程,println,new,out 来源: https://www.cnblogs.com/zhouqiangshuo/p/16511950.html