JUC概述
什么是锁
线程的虚假唤醒问题
Synchronized版本生产者消费者
JUC设置指定启动线程
JUC 生产者和消费者问题
集合类不安全
Callable
CyclicBarrier
Semaphore信号量
Semaphore
ReadWriteLock
阻塞队列
ReadWriteLock
SynchronousQueue同步队列
JUC并发编程
什么是JUC
线程和进程
进程
- 一个程序,例如:QQ.EXE 程序的集合
- 一个进程可以包含多个线程,至少包括一个
- Java默认有2个线程
线程
- 开一个进程Yypore,写字,自动保存(进程负责的)
- Java无法直接开启线程,只能通过调用本地方法开启
并发与并行
并发
- CPU 一核,在同一时间段内交替执行
并行
- CPU多核,多个线程同时执行
public class Test01 {
public static void main(String[] args) {
//获取CPU核数
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并发编程本质
- 充分利用CPU的资源
线程的6种状态
public static enum State {
//新生
NEW,
//运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//死亡
TERMINATED;
private State() {
}
}
wait和sleep区别
1、来自不同的类
- wait=》Object
- sleep=》Thread
2、关于锁的释放
- wait会释放锁,sleep不会释放锁
3、使用范围不同
- wait必须在同步代码块种使用
- sleep在任意地方使用
4、捕获异常不同
- wait不需要捕获异常
- sleep必须捕获异常
Lock锁和UnLock锁
Synchronized锁和Lock锁的区别
- Synchronized是一个内置关键字,Lock是一个类
- Synchronized无法判断锁的状态,Lock锁可以判断是否获取到了该锁
- Synchronized会自动释放锁,Lock要手动释放锁,若不释放锁,死锁
- Synchronized 线程一(获得锁,阻塞)、线程二(等待、傻傻的等);Lock锁不一定会等待下去
- Synchronized 可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置)
- Synchronized适合少量的代码同步问题,Lock锁适合大量的代码同步问题
锁是什么?如何判断锁的是谁?
- 锁对象、锁class模板
锁对象仅有一个且为类对象
/**
* 8锁,其实就是锁的8个问题
* 当此时锁对象位方法的调用者,只有一个对象,有谁优先获取的锁对象优先调用谁,释放后另一个获取锁对象依次排队
*/
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(()->{
phone.sendMes();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}, "B").start();
}
}
class Phone {
//synchronized锁的对象是方法的调用者
//俩个对象使用同一个锁,谁先拿到,谁先执行
public synchronized void sendMes() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
}
锁对象为2个且为类对象
import java.util.concurrent.TimeUnit;
/**
* 此时位俩个锁对象,不需要等待。
*/
public class Test2 {
public static void main(String[] args) {
Phone2 phone2 = new Phone2();
Phone2 phone3 = new Phone2();
new Thread(()->{
phone2.sendMes();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone3.call();
}, "B").start();
}
}
class Phone2 {
public synchronized void sendMes() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call() {
System.out.println("打电话");
}
//这里没有锁,不收同步代码块控制,首先调用
public void hello() {
System.out.println("hello");
}
}
锁对象仅有一个且为lass模板
- 此时判断有无类对象只需要判断同步方法是否被static修饰
import java.util.concurrent.TimeUnit;
/**
* 当方法倍static修饰时,此时锁对象位class模板
* 无论创建多少个对象调用方法此时锁对象只有一个即为class模板
*/
public class Test3 {
public static void main(String[] args) {
Phone3 phone3 = new Phone3();
//Phone3 phone31 = new Phone3();
new Thread(()->{
phone3.sendMes();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone3.call();
}, "B").start();
}
}
//Phone3 的唯一一个class对象
class Phone3 {
//synchronized锁对象是方法的调用者
// static静态方法
//类一加载就有了class模板,锁的是class
public static synchronized void sendMes() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call() {
System.out.println("打电话");
}
//这里没有锁,不收同步代码块控制,首先调用
public void hello() {
System.out.println("hello");
}
}
锁对象有2个,类对象和class模板
import java.util.concurrent.TimeUnit;
/**
* 当只有锁同一个对象时,才会等待
* 此种情况下锁对象为class模板和类对象
*/
public class Test4 {
public static void main(String[] args) {
Phone4 phone = new Phone4();
new Thread(()->{
phone.sendMes();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
phone.call();
}, "B").start();
}
}
//Phone3 的唯一一个class对象
class Phone4 {
//静态同步方法,锁的是class模板
public static synchronized void sendMes() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信");
}
//普通同步方法,锁的是对象
public synchronized void call() {
System.out.println("打电话");
}
}
线程的虚假唤醒问题
Synchronized版本生产者消费者
/**
* 生产者和消费者问题:线程通信问题 等待唤醒、通知唤醒
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
}, "A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
}, "B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
}, "C").start();
}
}
//判断等待、业务、通知
class Data {
private int number = 0;
//+1
public synchronized void increment() {
//使用while而不用if是为了避免线程的虚假唤醒问题
while (number != 0) {
//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println(Thread.currentThread().getName() + "->" + number);
//通知其它线程+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() {
while (number == 0) {
//等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(Thread.currentThread().getName() + "->" + number);
//通知其它线程-1完毕了
this.notifyAll();
}
}
JUC设置指定启动线程
package com.atguigu.pc;
import java.util.WeakHashMap;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* A执行完调用B,B执行完调用C,C执行完调用A
*/
public class C {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data3.printA();
}
}, "A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data3.printB();
}}, "B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data3.printC();
}
}, "C").start();
}
}
class Data3 {
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 {
//业务,判断、执行、通知
while (num != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "=>AAAAA");
//通知唤醒指定的人B
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() + "=>BBBBB");
//通知唤醒指定的人C
num = 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");
//通知唤醒指定的人C
num = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
JUC 生产者和消费者问题
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 生产者和消费者问题:线程通信问题 等待唤醒、通知唤醒
*/
public class B {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
}, "A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
}, "B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
}, "C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
}, "D").start();
}
}
//判断等待、业务、通知
/**
* condition.await(); 等待
* condition.signalAll(); 唤醒全部线程
*/
class Data2 {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//+1
public void increment() {
lock.lock();
try {
//使用while而不用if是为了避免线程的虚假唤醒问题
while (number != 0) {
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "->" + number);
//通知其它线程+1完毕了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//-1
public void decrement() {
lock.lock();
try {
//使用while而不用if是为了避免线程的虚假唤醒问题
while (number == 0) {
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + "->" + number);
//通知其它线程+1完毕了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
集合类不安全
List集合
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class ListTest {
public static void main(String[] args) {
//并发下集合不安全
/**
* 解决方案:
* 1、List<String> list = new Vector<>()
* 2、List<String> list = Collections.synchronizedList(new ArrayList<>());
* 3、List<String> list = new CopyOnWriteArrayList<>();
*/
List<String> list = new CopyOnWriteArrayList<>();
list.forEach(System.out :: println);
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();
}
}
}
Set集合
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class SetTest {
public static void main(String[] args) {
//Set<String> set = new HashSet<>();
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
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();
}
}
}
Map集合
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest {
public static void main(String[] args) {
//默认等价Map<String, String> map = new HashMap<>(16, 0.75);
//Map<String, String> map = new HashMap<>();
//Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
Map<String, String> map = new ConcurrentHashMap<>();
//加载因子,初始化容量
for (int i = 0; i < 30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
}
Callable
- 可以有返回值
- 可以抛出异常
- 方法不同 run()/call()
- 结果有缓存
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) {
new Thread().start(); //怎么启动call
MyThread thread = new MyThread();
//适配类
FutureTask futureTask = new FutureTask(thread);
new Thread(futureTask, "A").start();
//new Thread(futureTask, "A").start(); 结果方法入缓存
//获取call的返回结果
try {
Integer res = (Integer) futureTask.get();
System.out.println(res);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1024;
}
}
CyclicBarrier
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) {
/**
* 集齐7颗龙珠召唤神龙
*/
//召唤神龙线程
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();
}
}
}
Semaphore信号量
Semaphore
- 多个共享资源互斥使用,资源共享,控制最大线程数量
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* 类似于抢车位案例(6辆车抢3个车位)
*/
public class SemaphoreDemo {
public static void main(String[] args) {
//线程数量
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i < 6; i++) {
new Thread(()->{
// acquire()得到
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 {
//释放、会将当前的信号量释放+1,然后唤醒等待的线程
semaphore.release(); //释放
}
}, String.valueOf(i)).start();
}
}
}
ReadWriteLock
- 独占锁(写锁)一次只能被一个线程占有
- 共享锁(读锁)多个线程可以同时占有
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 独占锁(写锁)一次只能被一个线程占有
* 共享锁(读锁)多个线程可以同时占有
* 读-读 可以共存
* 读-写 不能共存
* 写-写 不能共存
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCacheLock myCache = new MyCacheLock();
//写入
for (int i = 1; i < 6; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp + "", temp + "");
}, String.valueOf(i)).start();
}
//读取
for (int i = 1; i < 6; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp + "");
}, String.valueOf(i)).start();
}
}
}
//自定义缓存
class MyCacheLock {
private volatile Map<String, Object> map = new HashMap<>();
//读写锁,更加细粒度的控制
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//读写的时候同时只希望有一个线程存在
public void put(String key, Object value) {
//添加写锁
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放写锁
readWriteLock.writeLock().unlock();
}
}
//写
public void get(String key) {
//读锁
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object value = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取OK");
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放读锁
readWriteLock.readLock().unlock();
}
}
}
阻塞队列
四组API
- 阻塞等待
- 超时等待
- 抛出异常
- 不会抛出异常
import java.sql.Time;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
//test1();
//test2();
/* try {
test3();
} catch (InterruptedException e) {
e.printStackTrace();
}*/
try {
test4();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//抛出异常
public static void test1() {
//队列大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
//查看队首元素
System.out.println(blockingQueue.element());
//System.out.println(blockingQueue.add("e")); 此时已满再添加抛出异常
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//System.out.println(blockingQueue.remove()); 此时队列为空抛出异常
}
public static void test2() {
//队列大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
//检测队首元素
System.out.println(blockingQueue.peek());
//System.out.println(blockingQueue.offer("c")); 不跑出异常,返回false
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
//System.out.println(blockingQueue.remove()); 此时队列为空 返回false
}
/**
* 等待(阻塞-一直阻塞)
*/
public static void test3() throws InterruptedException {
//队列大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
//一直阻塞
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//blockingQueue.put("d"); //队列无位置了一直阻塞
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take()); //没有这个元素,一直阻塞
}
/**
* 等待(阻塞-等待超时)
*/
public static void test4() throws InterruptedException {
//队列大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
//blockingQueue.offer("d", 2, TimeUnit.SECONDS); //队列满后等待2秒后退出
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS)); //等待2秒后超时退出
}
}
SynchronousQueue同步队列
- 没有容量,进去一个元素之后,必须等待取出来之后,才能往里面放一个元素
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* 同步队列
* 和其他的LockingQueue 不一样 SynchronousQueue不存储元素
* put了一个元素 必须从里面先take取出来,否则不能再put进去值
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
SynchronousQueue<String> stringSynchronousQueue = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println("put 1");
stringSynchronousQueue.put(Thread.currentThread().getName() + "1");
System.out.println("put 2");
stringSynchronousQueue.put(Thread.currentThread().getName() + "2");
System.out.println("put 3");
stringSynchronousQueue.put(Thread.currentThread().getName() + "3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + stringSynchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + stringSynchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + stringSynchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T2").start();
}
}