JUC及多线程,线程安全
JUC及多线程
返回到 Java开发知识汇总 目录 @程序员猴哥
1.什么是JUC
java.util.concurrent:核心并发工具类。
java.util.concurrent包含了许多线程安全,测试良好,高性能的并发模块。创建java.util.concurrent的目的就是要实现Collection框架对数据结构所执行的并发操作。
核心组件
Executor
ExecutorService
ScheduledExecutorService
Future
CountDownLatch
CyclicBarrier
Semaphore
ThreadFactory
BlockingQueue
DelayQueue
Locks
Phaser
传统程序员的4大利器:泛型,枚举,反射,注解
新程序员的4大利器:lambda表达式(str)->{return 1024},链式编程Array.asList(u1,u2),函数式接口FunctionInterface,stream流式编程list.stream().filter()
2.线程和进程
线程:程序语句快,是一个单独的资源类
进程:程序语句块的集合,通常一个应该程序至少包含一个进程.
3.Lock锁(重点)
同一时刻只允许有一个线程访问代码块的机制,是为了保证线程安全而生(存储数据的集合数据安全)。
创建锁的3种方式:继承Thread类,实现接口runable,callable
syschronized:队列,锁,通一时间只有一个队列消费
区别
1、synchronized是java关键字,而Lock是java中的一个接口
2、synchronized会自动释放锁,而Lock必须手动释放锁
3、synchronized是不可中断的,Lock可以中断也可以不中断
4、通过Lock可以知道线程有没有拿到锁,而synchronized不能
5、synchronized能锁住方法和代码块,而Lock只能锁住代码块
6、Lock可以使用读锁提高多线程读效率
7、synchronized是非公平锁,ReentranLock可以控制是否公平锁
1 lock():获取锁,如果锁被暂用则一直等待
2 unlock():释放锁
3 tryLock(): 注意返回类型是boolean,如果获取锁的时候锁被占用就返回false,否则返回true
4 tryLock(long time, TimeUnit unit):比起tryLock()就是给了一个时间期限,保证等待参数时间
5 lockInterruptibly():用该锁的获得方式,如果线程在获取锁的阶段进入了等待,那么可以中断此线程,先去做别的事
任何一个新的技术是对已有技术的优化和补充,lock condition 是
4.生产者和消费者问题
要判断是锁对象还是锁的类,静态类是同一个对象
使用if会存在虚假唤醒,需使用while
Synchronized(wait,notify),Lock(ReentrantLock,newCondition,lock,unlock,await,signalAll) 可以精准的控制线程唤醒:
5. 8锁现象 ,关于锁的8个问题
1.两个线程使用的是同一个对象(new Phone())。两个线程是一把锁!先调用的先执行!
2.两个线程使用的是2个对象(new Phone())。两个线程使用的是各自的锁!执行顺序有cpu策略决定。
3.两个线程使用的是2个类,两个线程使用的是相同的对象,同一把锁!执行顺序有cpu策略决定。
6.集合类不安全
多线程操作同一个集合时产生脏数据问题,需要添加锁或使用线程安全的集合。
LIst,set不安全
//Map<String,String> map=new HashMap<>();是线程不安全的 //List<String> list=new ArrayList<>();是线程不安全的 //List<String> list=new Vector<>(); //vector是线程安全的 //List<String> list= Collections.synchronizedList(new ArrayList<>()); //synchronizedList是线程安全的 //List<String> list=new CopyOnWriteArrayList<>(); //Set<String> list=new CopyOnWriteArraySet<>(); //Map<String,String> list=new ConcurrentHashMap<>();
CopyOnWriteArrayList是ArrayList的线程安全版本,内部也是通过数组实现,每次对数组的修改都完全拷贝一份新的数组来修改,修改完了再替换掉老数组,这样保证了只阻塞写操作,不阻塞读操作,实现读写分离。
7.Callable
创建多线程的一个接口,可以有返回值和能抛出异常
public class Test1 { public static void main(String[] args) throws ExecutionException, InterruptedException { // Thread(Runnable) // Thread(RunnableFuture) // Thread(FutureTask) MyThread myThread = new MyThread(); FutureTask task = new FutureTask(myThread); // 适配类 // 会打印几次 end new Thread(task,"A").start(); // 执行线程 new Thread(task,"B").start(); // 执行线程。细节1:结果缓存!效率提高N倍 System.out.println(task.get());// 获取返回值, get() } } class MyThread implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("end"); TimeUnit.SECONDS.sleep(3); return 1024; } }
8.常用的辅助类(必会)
1.CountDownLatch:减法计数器.等待所有的执行完成
CountDownLatch countDownLatch = new CountDownLatch(5);//减法计数器 for (int i = 0; i < 5; i++) { new Thread(()->{ System.out.println(Thread.currentThread().getName()+"go out"); countDownLatch.countDown(); //减1 },"a").start(); } countDownLatch.await(); //等待所有的执行完成 System.out.println("close door");//最后执行
2.CyclicBarrier加法计数器:等待直到7个线程执行完成
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{ System.out.println("召唤神龙成功"); }); for (int i = 0; i < 7; i++) { final int tem=i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+"shouji" +tem+"ge longzhu"); try { cyclicBarrier.await(); } catch (InterruptedException e) { throw new RuntimeException(e); } catch (BrokenBarrierException e) { throw new RuntimeException(e); } }).start(); }
3.Semaphore 同一时刻只允许3个线程执行,完成后另外3个线程再继续执行
// 线程数量:停车位有3个,开始停车3俩,等待1秒后释放,另外3个停车 Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 10; i++) { new Thread(()->{ try { semaphore.acquire();//抢到线程 若已满则等待 System.out.println("抢到线程"); } catch (InterruptedException e) { throw new RuntimeException(e); } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { throw new RuntimeException(e); }finally { semaphore.release(); //释放 } },String.valueOf(i)).start(); }
9.读写锁
ReadWriteLock:先批量一个个写入,然后再多线程读:writeLock(),readLock()
独占锁:写锁 一个一个写 单线程
共享锁:读锁,多个线程一起读
private volatile Map<String,Object> map=new HashMap<>(); //ReadWriteLock:先批量一个个写入,然后再随机读 private ReadWriteLock readWriteLock= new ReentrantReadWriteLock(); public void put(String key,Object value){ readWriteLock.writeLock().lock(); try{ System.out.println(Thread.currentThread().getName()+"write"+key); map.put(key,value); System.out.println(Thread.currentThread().getName()+"write 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()+"get"+key); Object o= map.get(key); System.out.println(Thread.currentThread().getName()+"get ok"); }catch (Exception e){ e.printStackTrace(); }finally { readWriteLock.readLock().unlock(); } } 执行结果: 1write1 1write ok 2write2 2write ok 0write0 0write ok 4write4 4write ok 3write3 3write ok 0get0 2get2 3get3 1get1 4get4 4get ok 0get ok 1get ok 2get ok 3get ok
10.阻塞队列
BlockingQueue
队列:FIFO 先进先出
写入队列元素,写入满了,需要阻塞
取队列元素,若元素是空的,则需阻塞
Colleciton:
1.List:
2.Set:
3.Queue:
3.1 blockingQueue:阻塞队列
3.2 AbstractQueue:非阻塞队列
3.3 Deque:双端队列
ArrayBlockingQueue arrayBlockingQueue=new ArrayBlockingQueue(5);
arrayBlockingQueue.add("a");
arrayBlockingQueue.remove(); //没有元素,跑异常
arrayBlockingQueue.put("a");
arrayBlockingQueue.take(); //没有一直等到死
arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b",2, TimeUnit.SECONDS);
arrayBlockingQueue.poll();
arrayBlockingQueue.poll(2,TimeUnit.SECONDS); //没有元素是等待2秒
使用场景:多线程并发处理,线程池
同步队列:SynchronousQueue,添加一个,取出1个
BlockingQueue<String> blockingQueue=new SynchronousQueue<>(); new Thread(()->{ System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName()); try { blockingQueue.put("1"); System.out.println(Thread.currentThread().getName()+"=>put 1"); blockingQueue.put("2"); System.out.println(Thread.currentThread().getName()+"=>put 2"); blockingQueue.put("3"); System.out.println(Thread.currentThread().getName()+"=>put 3"); } catch (InterruptedException e) { throw new RuntimeException(e); } },"t1").start(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take()); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take()); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take()); } catch (InterruptedException e) { throw new RuntimeException(e); } },"t2").start();
11. 线程池(重点)
// 线程池 总数可以管理 Executors原生三大方法 ExecutorService threadpool1 = Executors.newFixedThreadPool(10); // 固定大小 // ExecutorService threadpool2 = Executors.newCachedThreadPool(); //可以弹性伸缩的线程池,遇强则强 //ExecutorService threadpool3 = Executors.newSingleThreadExecutor(); // 只有一个 try { // 10个线程,会显示几个线程~ for (int i = 1; i <= 100; i++) { // 线程池,执行线程 threadpool1.execute(()->{ System.out.println(Thread.currentThread().getName()+" running..."); }); } } catch (Exception e) { e.printStackTrace(); } finally { // 线程池关闭 threadpool1.shutdown(); }
12. 四大函数式接口(必需掌握)
只有一个方法的接口称为函数式接口
13.Stream流式计算
可以将集合转为stream后利用stream过滤,转换,求和,求最大值,排序,limit等方式操作集合复制过滤,复制排序的方法。
14. ForkJoin
- Java 1 支持thread,synchronized。
- Java 5 引入了 thread pools, blocking queues, concurrent collections,locks, condition queues。
- Java 7 加入了fork-join库。有空闲时帮助消费
- Java 8 加入了 parallel streams。
15.异步回调
有返回值的异步回调:ComletableFuture
com=ComletableFuture.suppLyAsync(()->{return 1024;}); 无返回值的异步回调
16. JMM
Java内存模型,是一种概念
加锁解锁是同一把锁
工作线程,主内存
17. Volatile
1.保证可见性
2.不保证原子性:不可分割,同时成功同时失败
3.禁止指令重排
volatile 读取的时候去主内存中读取在最新值! private volatile static int num = 0; public static void main(String[] args) throws InterruptedException { // Main线程 new Thread(()->{ // 线程A 一秒后会停止! 0 while (num==0){ } }).start(); TimeUnit.SECONDS.sleep(1); num = 1; System.out.println(num); }