JUC并发
文章目录
1、线程和进程
线程和进程
进程:一个程序 程序的集合
一个进程往往可以包含多个线程,至少包含一个。
Java默认2个线程:main、GC
**java真的可以开启线程吗?**不能
//通过本地方法,调用底层c++,java无法直接操作硬件
private native void start0();
并发、并行
并发(多线程操作同一个资源)
- CPU一核,快速交替
并行
- CPU多核,多个线程同时执行;线程池
线程有几个状态
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用
2、Synchronized和Lock锁
ReentrantLock
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gjYwXV8a-1603975581250)(E:\桌面\images\1603803090(1)].jpg)
公平锁:十分公平,先来后到
非公平锁:不公平,可以由cpu调度
Synchronized 和 Lock 的区别
- Synchronized 内置的java关键字,Lock是一个java类
- Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
- Synchronized 会自动释放锁,Lock要手动unLock();
- Synchronized 线程1(获得锁,阻塞)线程2(等待),Lock锁就不一定会等下去;
- Synchronized 可重入锁,不可以中断,非公平,Lock,可重入锁,可以判断锁,非公平(可以自己设置)
- Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的代码同步
Synchronized问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OqN3Twew-1603975581254)(E:\桌面\images\1603808392(1)].png)
if改为while判断:
就是用if判断的话,唤醒后线程会从 wait 之后的代码开始运行,但是不会重新判断if条件,直接继续运行 if 代码块之后的代码,而如果使用 while 的话,也会从 wait 之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行 while 代码块之后的代码块,成立的话继续wait。
Lock锁问题 JUC版
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yG0UNepW-1603975581257)(E:\桌面\images\1603809475(1)].png)
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
condition.await();
condition.signalAll();
Condition 精准的通知和唤醒线程
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
testA(){
while(num!=1){
condition1.await();
}
num = 2;
condition2.siganl();
}
testB(){
while(num!=2){
condition2.await();
}
num = 2;
condition3.siganl();
}
testC(){
while(num!=3){
condition3.await();
}
num = 1;
condition1.siganl();
}
3、8锁现象
//1、标准情况下,先发短信
//2、sendSms延迟4秒,先发短信
public class Test1 {
public static void main(String[] args) {
Phone phone=new Phone();
//锁的存在
new Thread(()->{phone.sendSms();},"A").start();
try{
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(()->{phone.call();},"B").start();
}
}
class Phone{
//synchronized 锁的对象是方法的调用者!
//两个方法是同一把锁,谁先拿到谁执行
public synchronized void sendSms(){
try{
TimeUnit.SECONDS.sleep(4);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
//3,增加了一个普通方法,先执行发短信还是hello? 普通方法hello
//4,两个对象,两个同步方法,发短信还是打电话?打电话
public class Test2 {
public static void main(String[] args) {
//两个对象,两个调用者,两把锁!看时间
Phone2 phone1=new Phone2();
Phone2 phone2=new Phone2();
//锁的存在
new Thread(()->{phone1.sendSms();},"A").start();
try{
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(()->{phone2.call();},"B").start();
}
}
class Phone2{
//synchronized 锁的对象是方法的调用者!
//两个方法是同一把锁,谁先拿到谁执行
public synchronized void sendSms(){
try{
TimeUnit.SECONDS.sleep(4);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
//这里没有锁,不是同步方法,不受锁的影响
public void hello(){
System.out.println("hello");
}
}
//5,增加两个静态的同步方法,只有一个对象,先打印发短信还是打电话?发短信
//6,两个对象,增加两个静态的同步方法,只有一个对象,先打印发短信还是打电话?发短信
public class Test3 {
public static void main(String[] args) {
//两个对象的Class模板只有一个,static锁的模板
Phone3 phone1=new Phone3();
Phone3 phone2=new Phone3();
//锁的存在
new Thread(()->{phone1.sendSms();},"A").start();
try{
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(()->{phone2.call();},"B").start();
}
}
//Phone3 唯一的一个Class对象
class Phone3{
//synchronized 锁的对象是方法的调用者!
//static 静态方法,类一加载就有了,锁的是Class模板
public static synchronized void sendSms(){
try{
TimeUnit.SECONDS.sleep(4);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public static synchronized void call(){
System.out.println("打电话");
}
}
//7、1个静态同步方法,1个普通的同步方法,一个对象,先打印发短信还是打电话?打电话
//8、1个静态同步方法,1个普通的同步方法,两个对象,先打印发短信还是打电话?打电话
public class Test4 {
public static void main(String[] args) {
Phone4 phone1=new Phone4();
Phone4 phone2=new Phone4();
//锁的存在
new Thread(()->{phone1.sendSms();},"A").start();
try{
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(()->{phone2.call();},"B").start();
}
}
//Phone3 唯一的一个Class对象
class Phone4{
public static synchronized void sendSms(){
try{
TimeUnit.SECONDS.sleep(4);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("发短信");
}
public synchronized void call(){
System.out.println("打电话");
}
}
小结
new this 具体的一个对象
static Class 唯一的模板
4、集合类不安全
ConcurrentModificationException :并发修改异常
并发下ArrayList不安全
解决方案:
List<String> list = new Vector();
List<String> list = Collections.synchronizedList(new ArrayList<>());
List<String> list = new CopyOnWriteArratList<>();
CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略
多个线程调用的时候,list,读取的时候,固定的 ,写入(覆盖)
在写入的时候避免覆盖,造成数据问题
CopyOnWriteArratList和Vector区别
CopyOnWriteArratLis – Lock锁
Vector – synchronized
set不安全
Set<String> set = Collections.synchronizedSet(new HashSet<>());
List<String> list = new CopyOnWriteArratSet<>();
hashSet底层–hashMap
add set本质就是map key是无法重复的
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
map不安全
Set<String> set = Collections.synchronizedMap(new HashMap<>());
List<String> list = new ConcurrentHashMap<>();
5、Callable
可以有返回值,可以抛出异常,方法不同,run()/call()
代码
Callable<T>
T call(){}
//泛型与返回值要相同
new Thread(new FutureTask<V>(Callable)).start(); //适配器模式
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable=new MyCallable();
FutureTask futureTask =new FutureTask(myCallable);//适配类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();//结果会被缓存
Integer o = (Integer) futureTask.get();//返回结果 ,可能会产生阻塞,把他放到最后
//或者异步通信来处理
System.out.println(o);
}
}
class MyCallable implements Callable<Integer> {
@Override
public Integer call() {
System.out.println("call()");
return 1024;
}
}
细节:
- 有缓存
- 结果可能需要等待,会阻塞
6、 常用辅助类
6.1、CountDownLatch
减法计数器
public class CountDownLatchTest {
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()+"GO OUT");
countDownLatch.countDown();//数量-1
},String.valueOf(i)).start();
}
countDownLatch.await();//等计数器归零,然后再向下执行
System.out.println("close door");
}
}
每次有线程调用countDown()数量-1,假设计数器为0,countDownLatch.await()就会被唤醒,继续执行。
6.2、CyclicBarrier
加法计数器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7Xnz6KBQ-1603975581260)(E:\桌面\images\1603884516(1)].png)
public class CyclicBarrierTest {
public static void main(String[] args) {
//传Runnable
CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{
System.out.println("召唤成功");
});
for (int i = 1; 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();
}
}
}
6.3、Semaphore
Semaphore:信号量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bmx0tq3G-1603975581262)(E:\桌面\images\1603890296(1)].png)
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(2);
System.out.println(Thread.currentThread().getName()+"离开");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// release() 释放
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
//acquire() 获到,假设如果已经满了,等待,等待被释放为止
// release() 释放,会将当前的信号量释放+1,然后唤醒等待的线程
作用:多个共享资源互斥的使用,并发限流,控制最大的线程数
7、读写锁
ReadWriteLock
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6PlQ93u3-1603975581263)(E:\桌面\images\1603898258(1)].png)
/**
* 独占锁(写锁) 一次只能被一个线程占有
* 共享锁(读锁)多个线程可以同时占有
* ReadWriteLock
* 读-读 可以共存
* 读-写 不能共存
* 写-写 不能共存
*/
public class ReadWriteLockTest {
public static void main(String[] args) {
MyCache myCache=new MyCache();
for (int i = 1; i <= 7; i++) {
final int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
for (int i = 1; i <= 7; i++) {
final int temp = i;
new Thread(()->{
myCache.get(temp+"");
},String.valueOf(i)).start();
}
}
}
class MyCache{
private volatile 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");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.writeLock().unlock();
}
}
public void get(String key){
lock.readLock().lock();
try{
System.out.println(Thread.currentThread().getName()+"读取"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取Ok");
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.readLock().unlock();
}
}
}
8、阻塞队列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VzdbHzz5-1603975581264)(E:\桌面\images\1603900482(1)].png)
阻塞队列;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mxC9haao-1603975581265)(E:\桌面\images\1603900628(1)].png)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-drkRXKmR-1603975581267)(E:\桌面\images\1603902387(1)].png)
BlockingQueue
多线程并发处理,线程池
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D8wuf377-1603975581268)(E:\桌面\images\1603902614(1)].png)
使用队列
添加、移除
四组API
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer(),false | put() | offer(E,time,TimeUnit) |
移除 | remove() | poll(),null | take() | poll(time,TimeUnit) |
判断队首 | element() | peek() |
public class BQTest {
public static void main(String[] args) {
test1();
test2();
}
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());
//java.lang.IllegalStateException: Queue full 抛出异常
// System.out.println(blockingQueue.add("d"));
System.out.println("=============");
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//NoSuchElementException 抛出异常
//System.out.println(blockingQueue.element());
//java.util.NoSuchElementException 抛出异常
//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());
// false 不抛出异常
//System.out.println(blockingQueue.offer("d"));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
// null 不抛出异常
System.out.println(blockingQueue.poll());
}
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()); //没有这个元素,一直阻塞
}
}
9、同步队列
SynchronousQueue
没有容量,
进去一个元素,必须等待取出来之后,才能再往里面放一个元素(相当于容量1)
public class SynchronousQueueTest {
public static void main(String[] args) {
SynchronousQueue<String> 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()+"=>"+synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=>"+synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"=>"+synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
}
}