本文共 11149 字,大约阅读时间需要 37 分钟。
java.util.concurrent在并发编程中使用的工具类
API调用 工程师进程:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
线程:通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。
一个qq 进程,一边视频一边逛pyq 多线程
6种
public enum State { NEW,(新建) RUNNABLE,(准备就绪) BLOCKED,(阻塞) WAITING,(不见不散) TIMED_WAITING,(过时不候) TERMINATED;(终结)}
在哪睡的在哪醒,醒完继续往下走
功能都是当前线程暂停,有什么区别?
wait放开手去睡,放开手里的锁 sleep握紧手去睡,醒了手里还有锁并发:同一时刻多个线程在访问同一个资源 12306
并行:多项工作一起执行,之后再汇总 烧水泡面例子卖票程序 1、线程 操作 资源类 2、高内聚低耦合
Synchronized | Lock |
---|---|
自动上锁 | 手动上解锁 |
效率低 | 性能佳更灵活 |
锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。它们允许更灵活的结构,可能具有非常不同的属性,并且可能支持多个关联的条件对象。
Lock接口的实现:ReentrantLock可重入锁(API)newThread(new Runable(){ public void run()})
查看例子:LambdaDemo
要求 接口只有一个未实行方法 写法 拷贝小括号(),写死右箭头->,落地大括号{…}Foo foo = (int x,int y) -> { System.out.println("****hello lambda");};new Thread(() ->{ },"AAA").start();
函数式接口
lambda表达式,必须是函数式接口,必须只有一个方法 如果接口只有一个方法java默认它为函数式接口。 为了正确使用Lambda表达式,需要给接口加个注解:@FunctionalInterface
如有两个方法,立刻报错 Runnable接口为什么可以用lambda表达式? default方法
接口里在java8后容许有接口的实现,可以多个default方法默认实现 default int div(int x,int y) { return x/y; }静态方法实现
静态方法实现:接口新增 public static int sub(int x,int y){ return x-y; } 注意静态的叫类方法,能用foo去调吗?要改成Foo线程间通信:1、生产者+消费者2、通知等待唤醒机制
在进行判断是必须要用while,如果用if会出现虚假唤醒进程;设置初始值为0的变量
实现一个线程对变量增加1,一个线程对变量减1 交替10轮 01010101… **资源类 操作 线程 **class ShareDataOne { /** * 设置初始值为0的变量 * * 实现一个线程对变量增加1,一个线程对变量减1 * * 交替10轮 * * 资源类 操作 线程 */ //设置计数器参数 private int number = 0; //加法线程--先用synchronized public synchronized void incr() throws InterruptedException { //1、判断 // 在进行判断是必须要用while,如果用if(只判断一次)会出现虚假唤醒进程; while (number!=0) this.wait(); //2、干活 number++; System.out.println(Thread.currentThread().getName() + "\t" + number); //3、通知 this.notifyAll(); } //减法线程 public synchronized void decr() throws InterruptedException { //1、判断 while (number != 1) this.wait(); //2、干活 number--; System.out.println(Thread.currentThread().getName() + "\t" + number); //3、通知 this.notifyAll(); }}public class NotifyWaitDeno { public static void main(String[] args) { //拿取资源类 ShareDataOne shareDataOne = new ShareDataOne(); //生产者new Thread(()->{ for (int i = 0; i < 10; i++) { try { shareDataOne.incr(); } catch (InterruptedException e) { e.printStackTrace(); } } },"AA").start(); //消费者new Thread(()->{ for (int i = 0; i <10 ; i++) { try { shareDataOne.decr(); } catch (InterruptedException e) { e.printStackTrace(); } }},"BB").start(); }}
资源类 操作 线程
线程间:(while)判断 干活 通知
API调用Condition(钥匙)
jucdemo/Lock5_15.java
private Lock lock = new ReentrantLock(); private Condition c1 = lock.newCondition(); private Condition c2 = lock.newCondition(); private Condition c3 = lock.newCondition(); public void print5(int total) { lock.lock(); try { //1 判断 while(number != 1) { //A 就要停止 c1.await(); } //2 干活 for (int i = 1; i <=5; i++) { System.out.println(Thread.currentThread().getName()+"\t"+i+"\t total: "+total); } //3 通知 number = 2; c2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
集合类是不安全的
Arrayxxx在迭代的时候如果同时对其进行修改就会 抛出java.util.ConcurrentModificationException
异常 看源码没有synchronized
线程不安全 Vector 太老,效率低
Collections synchronized
效率低
CopyOnWriteArrayXXX(); //lock
Ctrl + F12 展示一个类的所有方法
输出字符串时int转String
// i+"" 注意占内存禁止使用String.valueOf(i)
类比
HashSet
set 底层new hashMap(key唯一,new object常量) //源码HashMap
Mapmap = new ConcurrentHashMap<>();//线程安全 对比 new SynchronizedHashMap<>();
ConcurrentHashMap | SynchronizedHashMap |
---|---|
并发 | 同步 |
行锁 | 表锁 |
老师和班长抢桌子上的手机
Synchonized作用范围 TimeUnitimport java.util.concurrent.TimeUnit; class Phone{ public synchronized void sendSMS() throws Exception { System.out.println("------sendSMS"); } public synchronized void sendEmail() throws Exception { System.out.println("------sendEmail"); } public void getHello() { System.out.println("------getHello"); } } /** * @Description:**锁的8个问题** 1 标准访问,先打印短信还是邮件 2 停4秒在短信方法内,先打印短信还是邮件 3 新增普通的hello方法,是先打短信还是hello 4 现在有两部手机,先打印短信还是邮件 5 两个静态同步方法,1部手机,先打印短信还是邮件 6 两个静态同步方法,2部手机,先打印短信还是邮件 7 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件 8 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件 * --------------------------------- */public class Lock_8{ public static void main(String[] args) throws Exception { Phone phone = new Phone(); Phone phone2 = new Phone(); new Thread(() -> { try { phone.sendSMS(); } catch (Exception e) { e.printStackTrace(); } }, "AA").start(); Thread.sleep(100); new Thread(() -> { try { phone.sendEmail(); //phone.getHello(); //phone2.sendEmail(); } catch (Exception e) { e.printStackTrace(); } }, "BB").start(); }}
这是一个函数式接口,因此可以用作lambda表达式或方法引用的赋值对象 。
如果只回答两个你连被问到juc的机会都没有
面试题:callable接口与runnable接口的区别?
答:(1)是否有返回值 (2)是否抛异常 (3)落地方法不一样,一个是run,一个是call创建新类MyThread实现runnable接口class MyThread implements Runnable{ @Override public void run() { }}新类MyThread2实现callable接口class MyThread2 implements Callable{ @Override public Integer call() throws Exception { return 200; } }
java多态,一个类可以实现多个接口!!
同一个FutureTask只计算一次FutureTaskft = new FutureTask (new MyThread());new Thread(ft, "AA").start();//运行成功后如何获得返回值?
get方法放到最后
6个同学(thread)陆续离开教室,班长(main)最后锁门
秦灭六国countDownLatch.countDown();//其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞)
countDownLatch.await();//当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
读写锁–教室控屏(一个人写多个人读)
Read–共享锁 Write–独占锁 volatile 不确定的private ReadWriteLock rwLock = new ReentrantReadWriteLock();
阻塞队列:等饭
线程1往阻塞队列里添加元素,线程2从阻塞队列里移除元素在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤起
ArrayBlockingQueue:由数组结构组成的有界阻塞队列。
LinkedBlockingQueue:由链表结构组成的有界(但大小默认值为integer.MAX_VALUE)阻塞队列。 无界 SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。
heap养老区周期长 原理ThreadPoolExecutor
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的销耗。
第二:提高响应速度。当任务到达时,任务可以不需要等待线程创建就能立即执行。 第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会销耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
编码实现,但是都不用 OOM
Executors.newFixedThreadPool(int):一池N线程Executors.newSingleThreadExecutor():一池一线程Executors.newCachedThreadPool():异步扩容,遇强则强 .execute(); .shutdown();
工作中需要手写线程池
线程池的拒绝策略
等待队列已经排满了,再也塞不下新任务了 同时,线程池中的max线程也达到了,无法继续为新任务服务。JDK内置的拒绝策略
以下内置拒绝策略均实现了RejectedExecutionHandle
接口 java.util.function
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算!”请按照给出数据,找出同时满足偶数ID且年龄大于24且用户名转为大写且用户名字母倒排序最后只输出一个用户名字public static void main(String[] args) { User u1 = new User(11,"a",23); User u2 = new User(12,"b",24); User u3 = new User(13,"c",22); User u4 = new User(14,"d",28); User u5 = new User(16,"e",26); Listlist = Arrays.asList(u1,u2,u3,u4,u5); list.stream().filter(p -> { return p.getId() % 2 == 0; }).filter(p -> { //过滤 return p.getAge() > 24; }).map(f -> { //toUpperCase()变大写 return f.getUserName().toUpperCase(); }).sorted((o1, o2) -> { //排序 return o2.compareTo(o1); //限制谁输出;增强for遍历输出 }).limit(1).forEach(System.out::println); //4种函数式接口实现 // R apply(T t); Function function = t -> {return t.length();}; System.out.println(function.apply("abc")); // boolean test(T t); Predicate predicate = t -> { return t.startsWith("a");}; System.out.println(predicate.test("a")); //void accept(T t); Consumer consumer = t -> { System.out.println(t);}; consumer.accept("java2020"); // T get(); Supplier supplier = () -> { return UUID.randomUUID().toString();}; System.out.println(supplier.get());; }
Fork:把一个复杂任务进行分拆,大事化小
Join:把分拆任务的结果进行合并ForkJoinPool分支合并池 类比=> 线程池
ForkJoinTask 类比=> FutureTask RecursiveTask递归任务:继承后可以实现递归(自己调自己)调用的任务 例如计算0+…+100分十个分支面试思路:多线程 - ThreadLocal - Spring事务(隔离级别)
ThreadLocal的作用主要是做数据隔离,填充的数据只属于当前线程,变量的数据对别的线程而言是相对隔离的,在多线程环境下,如何防止自己的变量被其它线程篡改。
1.使用场景:Spring事务,ThreadLocal包装SimpleDataFormat2.由于是弱引用:内存泄漏 使用remove()
ThreadLocal包装SimpleDataFormat
public class DateUtil { private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; private static ThreadLocal threadLocal = new ThreadLocal(); // 第一次调用get将返回null // 获取线程的变量副本,如果不覆盖initialValue,第一次get返回null, // 故需要初始化一个SimpleDateFormat,并set到threadLocal中 public static DateFormat getDateFormat() { DateFormat df = (DateFormat) threadLocal.get(); if (df == null) { df = new SimpleDateFormat(DATE_FORMAT); threadLocal.set(df); } return df; }}//apache commons-lang包的DateFormatUtils或者FastDateFormat实现,apache保证是线程安全的,并且更高效
转载地址:http://qvxab.baihongyu.com/