1 什么是JUC
就是java.util包下面的三个工具类
2 进程与线程
一个进程往往可以包含多个线程,至少包含一个,
java默认2个线程,一个main线程,一个GC线程
java 不能开启进程,开启进程是要调用本地方法,java是运行在虚拟机上的没有权限开启进行。
2.1并发与并行
并发–多个线程操作同一个资源
单核,模拟出来的多线程,交替执行
并行–多个线程可以同时执行
线程池
并发编程的本质:充分利用CPU的资源
2.2 线程状态
new Thread.State();
public enum State {
//新建
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
wait() 与 sleep()
1、来自不同的类
wait()–Object
sleep–Thread
2、关于锁的释放
wait()会释放锁
sleep()不会释放
3、使用的范围不同
wait() 必须在同步代码块中使用
sleep() 可以在任何地方睡觉
4、是否需要捕获异常
wait() 不需要捕获异常
sleep()必须捕获异常
3 Lock锁(重点)
synchronized
Lock接口有三个实现类
Lock与synchronized的区别重点
synchronized 是java关键字,Lock是一个类
synchronized 无法判断锁的状态,Lock可以判断是否获取到锁
synchronized 会自动释放锁,Lock必须要手动释放锁!如果不释放锁,会造成死锁
synchronized 线程1(获得锁,阻塞),线程2会等待,Lock中线程不会一直等待,会尝试获取锁TrayLok()
synchronized 可重入锁,不可中断,非公平;Lock 可重入,可以判断锁,非公平(可以自己设置)
synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码
锁是什么?如何判断锁的是谁
4 生产者和消费者问题?
synchronized版
/**线程之间通信问题:生产者和消费者问题 ! 等待唤醒 通知唤醒*/
/**线程之间交替执行 A B 操作同一个变量
* A num+1 线程之间通信
* B num-1*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i <10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decreament();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
/**判断等待业务通知*/
class Data{
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if (number != 0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
public synchronized void decreament() throws InterruptedException {
if (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
}
问题存在ABCD4个线程之后就会出现线程不安全!!
将if改为while条件后述情况就不会存在,由于是while()循环,所有被等待的线程在获取到cpu执行权利后一定会进行条件判断,不会出现两个生产者交替获得CPU执行权将number+1的情况(也不会出现两个消费者交替获得cpu执行权的情况)
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i <10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decreament();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i <10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decreament();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
/**判断等待业务通知*/
class Data{
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
while (number != 0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
public synchronized void decreament() throws InterruptedException {
while (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
this.notifyAll();
}
}
5 JUC版本生产者消费者问题
public class B {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decreament();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decreament();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
}
}
/**判断等待业务通知*/
class Data1 {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//加一操作
public synchronized void increment() throws InterruptedException {
lock.lock();
try {
while (number != 0) {
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//减一操作
public synchronized void decreament() throws InterruptedException {
lock.lock();
try {
while (number == 0) {
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
重点Condition如何 精准的通知和唤醒线程
6 精准控制唤醒线程
Condition精准控制
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public class C {
public static void main(String[] args) {
Data2 data2 = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data2.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data2.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data2.printC();
}
},"C").start();
}
}
class Data2{
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int num = 1;
public void printA(){
lock.lock();
try {
/**当num不等于1的时候,就等待*/
while (num != 1){
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAA");
num = 2;
/**处理完自己的逻辑,就唤醒指定的线程*/
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (num != 2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBBBBB");
num = 3;
/**处理完逻辑之后唤醒3号线程*/
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (num != 3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"=>CCCCC");
num = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
7 8锁对象
小结:锁的是两个东西:new this 具体的东西
static Class 锁的是全局唯一的 Class
什么是锁,如何判断锁的是谁
锁,锁的是对象,和class
1、synchronized 锁的对象是方法的调用者
两个方法用的是同一把锁,谁先拿到谁执行
2、两个静态方法,static静态方法,类一加载就存在,Class是模板
3、一个类只有一个唯一的Class对象,全局唯一,两个对象的Class类模板只有一个。
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendSms();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone.cell();
},"B").start();
}
}
class Phone{
//synchronized 锁的对象是方法的调用者
//两个方法用的是同一把锁,谁先拿到谁执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发消息");
}
public synchronized void cell(){
System.out.println("打电话");
}
}
8 集合类不安全
方案1:使用线程安全的集合Vector,但是这个集合内部直接使用synchronized保证线程安全,导致效率低。
方案2:使用集合类的顶层接口Collections的静态内部类
方案3:CopyOnWrite 写入时复制,多个线程调用list的时候,读取是固定的,写入的时候存在覆盖的情况
在写入的避免覆盖,造成数据错误问题。
它完成了读写分离,提高效率。
CopyOnWriteArrayList 比 Vector 效率高的地方,Vector所有的方法加了synchronized效率低,CopyOnWriteArrayList 底层加的Lock锁的方式保证线程安全。
public class LlistTest {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
Vector<String> list2 = new Vector<>();//解决方案一
List<String> list = Collections.synchronizedList(new ArrayList<>());//解决方案二
CopyOnWriteArrayList<String> list3 = 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();
}
}
}
8Map线程安全
new ConcurrentHashMap<>();
Collections.synchronizedMap(new HashMap<>());
9 Callable 多线程
区别:有返回值,可以抛异常,方法不同call()方法
String s = futureTask.get();可能会产生阻塞,
细节:
1、有缓存
2、结果可能等待,需要阻塞
public class CallableTest {
public static void main(String[] args) {
Mythread mythread = new Mythread();
//相当于一个适配类
FutureTask<String> futureTask = new FutureTask<>(mythread);
new Thread(futureTask,"A").start();
try {
String s = futureTask.get();
System.out.println(s+"返回的");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class Mythread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("call");
return "123456";
}
}
10CountDownLatch 减法计数器
原理:
downLatch.countDown();数量减一
downLatch.await();等待计数器归零,然后向下执行
每次线程调用countDown()数量就会减一,假设数量为0,countDownLatch.await()就会被唤醒,继续执行!
public class TestCountDown {
public static void main(String[] args) throws InterruptedException {
CountDownLatch downLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"go");
downLatch.countDown();
}).start();
}
downLatch.await();//计数器归零然后向下执行
System.out.println("关门");
}
}
11 cyclicBarrier 加法计数器
public class TestCyslic {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() -> {
System.out.println("到七个了");
});
for (int i = 0; i < 7; i++) {
final int temp = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"拿到"+temp+"个");
try {
/**等待*/
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
12 ReentrantReadWriteLock
独占锁(写锁) 一次只能被一个线程占有
共享锁(写锁) 多个线程可以同时占用
public class ReadWriteLockDemo {
public static void main(String[] args) {
//创建资源类
DataDemo dataDemo = new DataDemo();
//写数据
for (int i = 1; i < 6; i++) {
final int tmp = i;
new Thread(() -> {
dataDemo.write(tmp, "hello");
}, String.valueOf(i)).start();
}
//读数据
for (int i = 7; i < 11; i++) {
final int tmp = i;
new Thread(() -> {
dataDemo.ride(tmp);
}, String.valueOf(i)).start();
}
}
}
class DataDemo {
private HashMap map = new HashMap();
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void write(int key, String value) {
//更加细粒度的锁
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "插入数据");
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "插入OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
public void ride(int key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读取数据");
map.get(key);
System.out.println(Thread.currentThread().getName() + "读取OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
10 阻塞队列
FIFO:先进先出特点。
必须阻塞的情况
写入:如果队列满了,就不许阻塞等待。
取:队列如果是空的必须等待生产。
使用队列:
四组API
抛出异常
add()/remove()方法会抛出异常
/**抛出异常*/
public class BlockQueue {
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
//添加元素
System.out.println(queue.add("a"));
System.out.println(queue.add("b"));
System.out.println(queue.add("c"));
// System.out.println(queue.add("a"));
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
//抛出异常
System.out.println(queue.remove());
}
}
不抛出异常阻塞等待
offer()添加元素/poll()删除元素,peek()判断是头元素
private static void test2() {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
System.out.println(queue.offer("a"));
System.out.println(queue.offer("b"));
System.out.println(queue.offer("c"));
System.out.println(queue.offer("d"));
System.out.println(queue.peek()+"111");
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
}
阻塞等待
put()/take()
超时等待
/超时退出
public boolean offer(E e, long timeout, TimeUnit unit)
public E poll(long timeout, TimeUnit unit)
11 SynchronousQueue
同步队列:和其他队列BlockingQueue不一样,SynchronousQueue不存储元素
pub一个元素,必须从里面先取出来,否则不能put进去值。
没有容量,进去一个元素,必须等待取出来之后,才能往里面放一个元素。
put(),take()
12 线程池(重要)
创建线程的三种方式:
12.1 三大方法
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
12.2 七大参数:
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;
}
12.3 四种拒绝策略
四种拒绝策略就是RejectedExecutionHandler接口下的4个实现类
//new ThreadPoolExecutor.AbortPolicy(); /抛异常,不接受
//new ThreadPoolExecutor.CallerRunsPolicy());//主线程执行
//new ThreadPoolExecutor.DiscardPolicy());//队列满了,不会抛出异常
//new ThreadPoolExecutor.DiscardOldestPolicy());//队列满了,尝试和第一个任务竞争也不会抛出异常
最大的线程如何设计?
需要了解任务是cpu密集型还是io密集型
JMM
Volatile是java虚拟机提供轻量级同步机制
1、保证可见性
2、不保证原子性
3、禁止指令重排
什么是JMM:java内存模型
关于JMM的同步约定:
1、线程解锁前,必须把值刷回主存
2、线程加锁前,必须读取主存的最新值到工作内存中
3、加锁和解锁是同一把锁
线程中