1、进程和线程
1.1、JUC是什么?
juc
是java中java.util.concurrent
包的简称.它位于jdk的rt.jar中,是jdk的核心工具包之一。从字面上来理解就是java并发工具包。
1.2、进程与线程的区别
进程 :一个运行中的程序的集合; 一个进程往往可以包含多个线程,至少包含一个线程
线程 :线程(thread)是操作系统能够进行运算调度的最小单位。
1.3、并发与并行的区别
并发(多线程操作同一个资源,交替执行):CPU一核, 模拟出来多条线程,天下武功,唯快不破,快速交替
并行(多个人一起行走, 同时进行):CPU多核,多个线程同时进行 ; 使用线程池
操作
1.4 线程有六个状态
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
1.5、wait/sleep的区别
来自不同的类:wait来自object类, sleep来自线程类
关于锁的释放:wait会释放锁, sleep不会释放锁
使用的范围不同:wait必须在同步代码块中, sleep可以在任何地方睡眠
2、JUC的结构
正式学习时先了解五个部分:
2.1、Tools(工具类)
2.1.1、 辅助类CountDownLatch(闭锁)
CountDownLatch为常用类,它是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
2.1.2、 辅助类CyclicBarrier(栅栏)
CyclicBarrier为常用类,其是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
2.1.3、 辅助类Semaphore(信号量)
Semaphore为常用类,其是一个计数信号量,从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。
2.2、Executor(执行器)
Executor接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用 Executor 而不是显式地创建线程。
2.2.1、ScheduledExecutorService
ScheduledExecutorService继承自ExecutorService接口,可安排在给定的延迟后运行或定期执行的命令。
2.2.2、ScheduledThreadExecutor
ScheduledThreadPoolExecutor实现ScheduledExecutorService接口,可安排在给定的延迟后运行命令,或者定期执行命令。需要多个辅助线程时,或者要求 ThreadPoolExecutor 具有额外的灵活性或功能时,此类要优于 Timer。
2.2.3、ThreadPoolExecutor
ThreadPoolExecutor实现了AbstractExecutorService接口,也是一个 ExecutorService,它使用可能的几个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置。 线程池可以解决两个不同问题: 由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法。每个 ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。
2.3、Atomic(原子类)
其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。
2.3.1、基础类型:AtomicBoolean,AtomicInteger,AtomicLong
AtomicBoolean,AtomicInteger,AtomicLong是类似的,分别针对bool,interger,long的原子类。
2.3.2、数组:AtomicIntegerArray,AtomicLongArray,BooleanArray
AtomicIntegerArray,AtomicLongArray,AtomicBooleanArray是数组原子类。
2.4、Locks(锁包)
锁包是JDK提供的锁机制,相比synchronized关键字来进行同步锁,功能更加强大,它为锁提供了一个框架,该框架允许更灵活地使用锁。
2.4.1、Condition
Condition
将 Object
监视器方法(wait
、notify
和 notifyAll
)分解成截然不同的对象,以便通过将这些对象与任意 Lock
实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock
替代了 synchronized
方法和语句的使用,Condition
替代了 Object 监视器方法的使用。
2.4.2、Lock
Lock
实现提供了比使用 synchronized
方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition
对象。
锁是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问。一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁。不过,某些锁可能允许对共享资源并发访问,如 ReadWriteLock
的读取锁。
synchronized
方法或语句的使用提供了对与每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。
2.4.3、ReadWriteLock
ReadWriteLock
维护了一对相关的锁
,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁
可以由多个 reader 线程同时保持。写入锁
是独占的。
2.5、Collections(集合类)
主要是提供线程安全的集合
2.5.1、BlockingQueue
BlockingQueue实现是线程安全的。所有排队方法都使用内部锁或其他形式的并发控制以原子方式实现其效果。
2.5.2、CopyOnWriteArrayList
ArrayList 的一个线程安全的变体,其中所有可变操作(add、set 等等)都是通过对底层数组进行一次新的复制来实现的。这一般需要很大的开销,但是当遍历操作的数量大大超过可变操作的数量时,这种方法可能比其他替代方法更 有效。在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时,它也很有用。
2.5.3、CopyOnWriteArraySet
对其所有操作使用内部CopyOnWriteArrayList的Set。即将所有操作转发至CopyOnWriteArayList来进行操作,能够保证线程安全。在add时,会调用addIfAbsent,由于每次add时都要进行数组遍历,因此性能会略低于CopyOnWriteArrayList。
2.5.4、ConcurrentHashMap
是线程安全HashMap的。ConcurrentHashMap在JDK 7之前是通过Lock和segment(分段锁)实现,JDK 8 之后改为CAS+synchronized来保证并发安全。
3、JUC常用辅助类
3.1、CountDownLatch(减少计数)
public class CountDownLatchMain {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 同学出教室了");
countDownLatch.countDown();
}, Integer.toString(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + " 班长锁门了");
}
}
3.2、CyclicBarrier(循环栅栏)
public class CyclicBarrierMain {
private static final int COUNT = 7;
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(COUNT, () -> {
System.out.println("***集齐了" + COUNT + "颗龙珠***");
});
for (int i = 1; i <= COUNT; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 龙珠找到了");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, Integer.toString(i)).start();
}
}
}
3.3、Semaphore(信号量)
public class SemaphoreMain {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3, true);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
// 占用线程
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 进入了车位");
Thread.sleep(1000 * new Random().nextInt(5));
System.out.println(Thread.currentThread().getName() + " 离开了车位***");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放线程
semaphore.release();
}
}, Integer.toString(i)).start();
}
}
}
4、Callable接口
public class Demo implements Callable {
@Override
public Object call() throws Exception {
return 200;
}
}
public class CallableThreadMain {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 使用Callable接口创建线程,FutureTask:未来任务
FutureTask<Integer> futureTask = new FutureTask<Integer>(new Demo());
new Thread(futureTask, "AA").start();
while (!futureTask.isDone()) {
System.out.println("等待结果。。。");
}
System.out.println(futureTask.get());
}
}
5、线程安全集合
5.1、ConcurrentHashMap
5.2、CopyOnWriteArrayList
5.3、CopyOnWriteArraySet
6、Lock锁
6.1、ReentrantLock(可重入的互斥锁)
public class Ticket {
/**
* 一个可重入的互斥锁 Lock
*/
private final ReentrantLock lock = new ReentrantLock();
private int ticketCount = 30;
public void buy() {
// 上锁
lock.lock();
try {
if (ticketCount > 0) {
System.out.println(Thread.currentThread().getName() + ":买到第" + ticketCount + "张票!");
ticketCount --;
}
} finally {
// 解锁
lock.unlock();
}
}
}
6.2、ReentrantReadWriteLock(读写锁)
public class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
private final ReadWriteLock writeLock = new ReentrantReadWriteLock();
public Object get(String key) {
writeLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " 读取数据中 :: " + key);
Thread.sleep(300);
Object d = map.get(key);
System.out.println(Thread.currentThread().getName() + " 读取数据成功 :: " + key);
return d;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.readLock().unlock();
}
return null;
}
public void put(String key, Object data) {
writeLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " 写入数据中 :: " + key);
Thread.sleep(300);
map.put(key, data);
System.out.println(Thread.currentThread().getName() + " 写入数据成功 :: " + key);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.readLock().unlock();
}
}
}
7、阻塞队列
7.1、BlockingQueue
/**
* 抛出异常
*/
public static void test1() {
//队列的大小
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
//java.lang.IllegalStateException: Queue full 抛出异常
//System.out.println(arrayBlockingQueue.add("d"));
System.out.println("**************");
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
//java.util.NoSuchElementException 抛出异常
//System.out.println(arrayBlockingQueue.remove());
}
/**
*有返回值,不抛出异常
*/
public static void test2(){
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
//System.out.println(arrayBlockingQueue.offer("d")); // false 不抛出异常!
System.out.println("*****");
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll()); // null 不抛出异常!
}
/**
*等待,阻塞(一直阻塞)
*/
public static void test3() throws InterruptedException {
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
//一直阻塞
arrayBlockingQueue.put("a");
arrayBlockingQueue.put("b");
arrayBlockingQueue.put("c");
//arrayBlockingQueue.put("d"); //队列没有位置了,一直阻塞
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take()); //没有数据了,一直阻塞
}
/**
*等待,阻塞(等待超时)
*/
public static void test4() throws InterruptedException {
ArrayBlockingQueue<Object> arrayBlockingQueue = new ArrayBlockingQueue<>(3);
arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b");
arrayBlockingQueue.offer("c");
arrayBlockingQueue.offer("d",2, TimeUnit.SECONDS); //等待两秒,超时退出
System.out.println("******");
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
arrayBlockingQueue.poll(2, TimeUnit.SECONDS); //等待两秒,超时退出
}
7.2、SynchronousQueue
/**
* 同步队列
* SynchronousQueue 和 BlockingQueue 不一样,SynchronousQueue 不存储元素
* put一个元素,必须从里面take取出来,否则不能再put进去值!(存一个,取一个,循环)
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
//存一个元素,取一个元素 循环
SynchronousQueue<Object> synchronousQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "->put 1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName() + "->put 2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName() + "->put 3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T1").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "->take " + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "->take " + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "->take " + synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T2").start();
}
}
8、ThreadPoolExecutor(线程池)
8.1、参数介绍
corePoolSize
- 池中所保存的线程数,包括空闲线程。
maximumPoolSize
- 池中允许的最大线程数。
keepAliveTime
- 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit
- keepAliveTime 参数的时间单位。
workQueue
- 执行前用于保持任务的队列。此队列仅保持由 execute
方法提交的 Runnable
任务。
threadFactory
- 执行程序创建新线程时使用的工厂。
handler
- 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
8.2、使用
/**
* 自定义ThreadPoolExecutor规则
*/
public class MyThreadPoolExecutor extends ThreadPoolExecutor {
private final static BlockingQueue<Runnable> BLOCKING_QUEUE = new LinkedBlockingQueue<>();
private final static ThreadFactory THREAD_FACTORY = Executors.defaultThreadFactory();
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize) {
super(corePoolSize, maximumPoolSize, 60L, TimeUnit.SECONDS, BLOCKING_QUEUE, THREAD_FACTORY, defaultHandler);
}
}
/**
* 测试启动
*/
public class ThreadPoolExecutorMain {
public static void main(String[] args) {
MyThreadPoolExecutor threadPool = new MyThreadPoolExecutor(10, Integer.MAX_VALUE);
for (int i = 0; i < 100; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " :: ");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
threadPool.shutdown();
}
}
8.3、拒绝策略
new ThreadPoolExecutor.AbortPolicy() //默认拒绝策略 银行满了,还有人进来,不处理这个人,抛出异常
new ThreadPoolExecutor.CallerRunsPolicy() //哪来的去哪里!
new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试和最早的竞争,也不会抛出异常!