JUC并发编程
1.Lock锁(重点)
公平锁和非公平锁
公平锁:公平,先来后到
非公平锁:不公平,可以插队(默认)
Lock锁用法:
l.lock()加锁 try(业务代码)-catch-finally 中 l.unlock() 解锁
package com.wei.demo01;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
*并发:多线程操作同一个资源类,把资源类丢入线程
*/
public class SaleTicketDemo2 {
public static void main(String[] args) {
Ticket ticket = new Ticket();
//@FunctionalInterface函数式接口
//lambda表达式 (参数)->{代码}
new Thread(()->{ for (int i = 0; i <60 ; i++) ticket.sale(); },"A").start();
new Thread(()->{ for (int i = 0; i <60 ; i++) ticket.sale(); },"B").start();
new Thread(()->{ for (int i = 0; i <60 ; i++) ticket.sale(); },"C").start();
}
//资源类
static class Ticket{
int number = 30;
//ReentrantLock 可重入锁
Lock lock=new ReentrantLock();
public void sale(){
//加锁
lock.lock();
try {
if(number >0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"张票,剩余"+number);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//解锁
lock.unlock();
}
}
}
}
synchronized和Lock锁的区别
1.synchronized 内置关键字 Lock是一个java类
2.synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
3.synchronized 会自动释放锁 Lock必须手动释放(l.unLock)如果不是放,死锁
4.synchronized 线程1(获取锁 阻塞),线程2(等待 一直等待)。Lock锁不一定等待(trylock)
5.synchronized 可重入锁,不可中断,非公平。 Lock 可重入锁,可中断,可设置公平
6.synchronized 适合锁少量的代码同步问题 Lock 适合锁大量的代码同步问题
锁是什么,如何判断锁的是谁?
2.生产者和消费者问题
(面试:单例模式 、排序算法 、生消模式、 死锁)
1.生产者和消费者问题synchronized 版**(只适合AB两个线程)**
package com.wei.pc;
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.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
//判断等待 业务 通知
static 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);
//通知其他线程 +1完毕
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程 +1完毕
this.notifyAll();
}
}
}
虚假唤醒问题 解决:把if换成while
2.新版(Lock版)生产者消费者模式
condition 实现精准通知唤醒
package com.wei.pc;
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) {
Data data = new Data();
new Thread(()->{
try {
for (int i = 0; i <10 ; i++) {
data.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
for (int i = 0; i <10 ; i++) {
data.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C").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();} },"D").start();
}
//判断等待 业务 通知
// condition.await(); 等待
// condition.signalAll(); 唤醒全部
static class Data{ //资源
private int number = 0;
//+1
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (number!=0){
//等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
condition.signalAll();//唤醒
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//-1
public void decrement() {
lock.lock();
try {
while (number==0){
//等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知其他线程 +1完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
condition 实现精准通知唤醒
package com.wei.pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class C {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{ for (int i = 0; i <10 ; i++) { data.printA(); } },"A").start();
new Thread(()->{ for (int i = 0; i <10 ; i++) { data.printB(); } },"B").start();
new Thread(()->{ for (int i = 0; i <10 ; i++) { data.printC(); } },"C").start();
}
}
//资源类
class Data{
private Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
private int number =1; //1A 2B 3C
public void printA(){
lock.lock();
try {
while (number!=1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"AAAAAAA");
//唤醒指定的人
number=2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (number!=2){
//等待
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"BBBBBB");
number=3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();//加锁
try {
while (number!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"CCCCCCCC");
number=1;
condition1.signal();//唤醒
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//解锁 防止死锁
}
}
}
3.8锁
4.集合不安全(解决方案)
1.list不安全
package com.wei.unsafe;
import java.lang.reflect.Array;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
//java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
public static void main(String[] args) {
/**
* 并发下ArrayList是不安全的
* 解决方法:
* 1. List<String> list = new Vector();
* 2.List<String> list = Collections.synchronizedList(new ArrayList<>());Collections自带的安全转换
*3.List<String> list = new CopyOnWriteArrayList<>();
*/
//CopyOnWrite 写入时复制 COW 计算机领域的一种优化策略
//多个线程调用 list 写入时候会覆盖,线程不安全
// 可以读写分离
//CopyOnWriteArrayList 比Vector 好在哪
//Vector 是synchronized修饰的 效率低 CopyOnWriteArrayList是lock锁
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <10 ; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
2.set不安全
解决方法:同List
package com.wei.unsafe;
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 = 1; i <=30 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
hashset底层是什么?(Hashmap)
public HashSet() {
map = new HashMap<>();
}
//hashset.add()
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
3.map不安全
package com.wei.unsafe;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest {
public static void main(String[] args) {
//HashMap<String, String> map = new HashMap<>();
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i <10 ; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
ConcurrentHashMap底层原理
自己翻api文档
hashmap底层原理
加载因子 初始化容量
5.callable
@FunctionalInterface
public interface Callable<V>返回结果并可能引发异常的任务。 实现者定义一个没有参数的单一方法,称为call 。
Callable接口类似于Runnable ,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,A Runnable不返回结果,也不能抛出被检查的异常。
该Executors类包含的实用方法,从其他普通形式转换为Callable类。
1.可以有返回值
2.可以抛出异常
3.方法不同,Runable是run() Callable是call()
用法:
package com.wei.callable;
import com.wei.pc.B;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//怎么启动callable
MyThread myThread = new MyThread();
//适配类
FutureTask futureTask = new FutureTask(myThread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start(); //只执行一个 结果会被缓存,效率高
System.out.printf("返回值:"+(Integer) futureTask.get());
//get方法会产生阻塞 放到最后(或者使用异步通信来处理)
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("Callable");
return 1024;
}
}
细节:
1、有缓存
2、结果可能需要等待,会阻塞!
6、常用的辅助类
6.1、CountDownLatch
6.2、CyclicBarrier
").start();
new Thread(futureTask,“B”).start(); //只执行一个 结果会被缓存,效率高
System.out.printf(“返回值:”+(Integer) futureTask.get());
//get方法会产生阻塞 放到最后(或者使用异步通信来处理)
}
}
class MyThread implements Callable{
@Override
public Integer call() throws Exception {
System.out.println(“Callable”);
return 1024;
}
}
细节:
1、有缓存
2、结果可能需要等待,会阻塞!
### 6、常用的辅助类
#### 6.1、CountDownLatch
#### 6.2、CyclicBarrier
#### 6.3、Semaphore