前言:函数式编程
函数式接口Function interface: 任何接口,如果只包含唯一 一个抽象方法,那么他就是一个函数式接口
如:Runnable接口 当中只有一个抽象方法,他就是函数式接口 如果有两个及以上的抽象方法,它就不是函数式接口
lambda 表达式:
//Lambda 表达式class T{public void run(){new Thread(()->System.out.println()).start();new Thread(new Runnable(){@Overridepublic void run() {System.out.println();}}).start(); ;}}
一.多线程基础:
1.1线程与进程
1.1.1进程:
进程是对应操作系统而言的,一个应用程序或者服务,操作系统主要的任务就是进程调度。目前的操作系统都支持多进程的任务调度。
1.1.2线程:
线程是进程的细分,一个进程可以包含多条线程,线程也叫轻量级进程。CPU调度的最小单位是线程
1.2 线程两种创建方式:
1.2.1 继承Thread类
1.定义类来继承Thread根据自己的要求
2.重写run()
3.创建并启动线程
1.2.2实现Runnable接口:
实现Runnable然后使用Thread去用start方法 实现的是静态代理<br /> 创建Runnable实现类( Task任务 ):<br />
//线程任务class WashFeet implements Runnable{@Overridepublic void run() {while (true)System.out.println("洗脚.....");}}创建线程并启动:public class SkyAndEarth {public static void main(String[] args) {Thread son = new Thread( new WashFeet() );son.start();while (true){System.out.println("正在营业....");}}
<br />**还有一种实现Callable接口: 在第六章(了解)**
1.2.3 两种方式对比
方式一,线程对象和线程任务是耦合的,方式二,线程对象和线程任务是分离的<br /> 方法一,扩展性不好,类单继承的。 方式二,扩展性更好,接口是多实现。<br /> 方法一,调用线程方法直接 ,方式二,不可直接调用线程方法,需要先调用 Thread.currentThread(),<br /> 这个方法的作用是拿到与当前线程任务绑定的线程对象<br />
1.3 线程常见方法:
<br />Thread.currentThread() //拿到线程本身<br />.getState() 获得线程状态<br />start() 启动线程。<br />.isAlive(); 查看是否还活着<br /> Thread.sleep( long ms ) 睡眠 暂停执行,直到睡眠时间到。<br /> setName()/getName() 取名字/获得名字,如果没有设置名字,默认名字为Thread-(0-n)<br />Thread.yield()线程礼让. 让出cpu执行时间片,自己进入就绪状态,再次等待调度。<br />join() 同步,加入线程线程中<br /> setDeamon(boolean xx)/isDeamon()设置守护线程/判断守护线程<br />守护线程:c()垃圾回收 虚拟机必须确保用户线程执行完毕 <br /> 虚拟机不用等待守护线程执行完毕 如 后台记录操作日志、监控内存、垃圾回收用户线程:执行完就结束了<br /> setPriority(int xx)/getPriority() 设置/获取优先级 ,<br /> 默认三个 MIN_PRIORITY = 1;NORM_PRIORITY = 5;MAX_PRIORITY = 10;<br /> ava提供一个线程调度器(控制cpu) 用来监控程序启动后进入就绪状态的所有线程;<br /> 线程调度按照优先级决定调度哪个线程来执行<br /> 线程优先级1~10 线程优先级越高cpu执行时不一定会调它 只是他的权重会变大;<br />
1.4 理解同步与异步
同步:一个线程全部做完以后,另一个线程才开始执行,也就是一个线程的开始总是接着另一个线程的结尾。<br /> 异步:一个线程不需要等待,另一个线程执行完毕后,才执行,从宏观上看线程是同时执行,但是微观上是一个线程执行一点点后,另外一个线程又执行一点点,这样交替执行。线程默认是异步的,同步需要自己控制<br /> 多线程好处: 可以充分利用CPU,尤其是在处理阻塞问题的时候,使用多线程可以提高效率<br />
二.线程同步:
2.1 为什么要线程同步
就是为了数据一致,数据的安全性。<br />
2.2.1 同步代码块
class Window implements Runnable{//模拟100张票static int total = 100;@Overridepublic void run() {while (true){synchronized( "" ){ // 小括号()里需要一个对象,这个对对象必须满足,不同线程进入时是同一个对象。这样才有互斥性。if(total>0) {total--;//没时间了System.out.println(Thread.currentThread().getName() + "卖出1张,还剩" + total);}else{break;}}}}}
2.2.2 同步方法
和同步代码块原理基本一致也是使用synchronized 只是把同步范围扩大至整个方法,也就是锁的粒度更粗。<br />
//卖票软件class SoftWare{private static int total = 100 ;public static synchronized void sales(){fwefwwfewfwefwfewfif( total>0 ){total--;System.out.println( Thread.currentThread().getName() +"销售1张,还剩余"+total);}}public static int size(){return total;}}
2.2.3 同步不安全的集合
static <T> List<T> synchronizedList(List<T> list) <br /> 返回指定列表支持的同步(线程安全的)列表。static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)<br /> 返回由指定映射支持的同步(线程安全的)映射。static <T> Set<T> synchronizedSet(Set<T> s)<br /> 返回指定 set 支持的同步(线程安全的)set。<br /> <br /> 以 synchronizedList(List<T> list) 为例, 实现原理就是使用 同步代码块,这方法返回值实际上是 SynchronizedList 的实例,<br /> 这个实例套娃使用了 出入参数不安全的集合 ,在不安全外围包裹了一个 同步代码块<br />
2.2.4 死锁问题[高频面试]
<br />**死锁**:多个线程各自占着共享资源 并且互相等待其他线程占有的资源才可以运行,导致两者或者多个线程都在等待对象释放资源停止的这种情形 <br />**死锁产生的必要条件:**<br /> **1.互斥条件:一个资源只能被一个线程所使用**<br />** 2.请求与保持条件:一个线程因请求资源而阻塞时,以获取的资源保持不放**<br />** 3.不剥夺条件:线程获取资源,在未使用完成时,不能强行剥夺**<br />** 4.循环等待:若干线程之间形成一种头尾衔接的循环等待资源的关系**<br />** 上面四个死锁出现的必要条件,我们只要想办法破坏其中一种,就可以避免死锁**
class test{public static void main(String[] args){Object o1 = new Object();Object o2 = new Object();Thread son1 = new Thread( new H(o1,o2) );son1.start();Thread son2 = new Thread( new Boo(o1,o2) );son2.start();}}//死锁class H implements Runnable{Object o1 = null;Object o2 = null;public H(Object o1,Object o2){this.o1=o1;this.o2=o2;}@Overridepublic void run() {synchronized (o1){System.out.println( "Foo线程获得o1,想去锁o2");//可选 主要是为其他线程争取时间try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2){System.out.println("Foo线程获得o2");}System.out.println("Foo释放o2");}System.out.println("Foo 释放o1");}}class Boo implements Runnable{Object o1 = null;Object o2 =null;public Boo(Object o1, Object o2) {this.o1 = o1;this.o2 = o2;}@Overridepublic void run() {synchronized (o2){System.out.println( "Boo线程获得o2,想去锁o1");synchronized (o1){System.out.println("Boo线程获得o1");}System.out.println("Boo释放o1");}System.out.println("Boo 释放o2");}}
三.线程生命周期[高频面试]
创建 就绪 -(阻塞)- 运行 结束
四..JUC锁[高频面试]
4.1 锁分类
锁的作用就是保证数据一致性,但是往往就会牺牲效率,所以使用锁需要自己做好平衡和取舍。<br /> 同等情况下,当然使用性能更好的锁,是最好的方案。<br />
4.1.1 悲观锁
怀疑任何情况都会出现并发问题,所以在设计的时候 就默认锁定,jdk1.5前就是 synchronized ,<br /> 早期说这个性能有问题(经过优化现在一般不去比较了)。<br /> 在JDK1.5新的解决方法是提供了Lock接口和他的实现类。例如 ReentrantLock ReentrantReadWriteLock等。<br />
4.1.2 乐观锁
乐观锁就是和悲观锁相反的思想,就是先不怀疑存在并发问题,如果确有存在再解决,<br /> 通常需要为并发修改的数据设定一个版本号,<br /> 修改前对一下先前获取的版本号,修改时在比较版本号如果一致才修改,<br /> 不一致说明有版本变化不可修改。CAS算法就是一种乐观锁算法,<br /> 它是硬件层面实现的,把多语句指令和为一个指令,确保了原子操作。<br /> Compare And Swap( 比较 和 交换 ) V (内存值) E(期望值) N(新值) 当且仅当 V==E时 才将 V=N 。
4.1.3Lock 锁
java1.5之后的更为强大的同步机制,通过显示定义同步锁对象来实现同步,同步锁使用Lock对象来<br /> Lock接口和他的实现类。例如 **ReentrantLock ReentrantReadWriteLoc**k等Lock和对比 synchronizedlock 是显式锁 synchroized是隐式锁 lock只能锁代码块 使用lock可以提高效率<br />synchronized 关键字 ReentrantLock 类(jdk1.5新引入)<br /> 都是重入锁(ReentrantLock 功能强大 提供了更多的方法,以及实现公平锁策略)<br /> synchronized 上锁与解锁自动完成 , ReentrantLock 需要自己上锁和解锁。
class L implements Runnable{public static void main(String[] args) {Thread T1 = new Thread(new L());T1.setName("黄牛");Thread T2 = new Thread(new L());T2.setName("商家");Thread T3 = new Thread(new L());T3.setName("学生");T3.setPriority(10);T1.start();T2.start();T3.start();}static int num =100;static Lock lock = new ReentrantLock();@Overridepublic void run() {while(true){try {lock.lock();if (num>0){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}num--;System.out.println(Thread.currentThread().getName()+"抢到一张票,现在剩余"+num+"张票");}else{break;}}finally {lock.unlock();}}}}
五.线程通信;
什么是线程通信:协调多个线程有序访问某些资源。<br /> 线程通信方法:这些方法必须使用在存在 synchronized 代码块内 或者 synchronized 方法中。<br /> wait():void , 它是Object的方法,不是线程提供的方法,让执行这个wait()调用的线程等待。无限期等待,直到有被唤醒。<br /> wait( long ms ) : 等待指定的毫米数,如果没有被唤醒,则自动唤醒<br /> wait( long ms, int naos) : 更精确的等待时间<br /> notify(): 唤醒等待在该对象上的线程(只会唤醒一个)。<br /> notifyAll(): 唤醒全部等待在该对象上的全部线程。<br /> 开发协作模式”生产者/消费者”===》
//线程通信方法class WaitAndNotifyCase {public static void main(String[] args) throws InterruptedException {Object obj = new Object();Thread son1 = new Thread( new Foo(obj) );son1.setName("张三");Thread son2 = new Thread( new Foo(obj) );son2.setName("李四");son1.start();son2.start();//让主线程等5s后去唤醒其他线程Thread.sleep(5000);synchronized (obj){//obj.notify();//唤醒一个obj.notifyAll();//唤醒全部}}}class Foo implements Runnable{Object obj = null;public Foo(Object obj) {this.obj = obj;}@Overridepublic void run() {synchronized (obj){System.out.println(Thread.currentThread().getName()+ "执行开始...");try {obj.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ "执行完毕...");}}}
管程法
生产者:负责生产(可能是方法、对象、线程、进程)<br /> 消费者:负责消费(可能是方法、对象、线程、进程)<br /> 缓冲区:消费者不能直接使用生产者数据需要有一个缓冲区<br />开发协作模式"生产者/消费者”===》
//生产者消费者模型 管程法//容器class Container{Object[] data = new Object[10];int size;public synchronized void add (Object obj){while(size==data.length){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}data[size++]=obj;System.out.println(Thread.currentThread().getName()+"生产了"+obj);this.notifyAll();}public synchronized Object get(){while (size==0){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}Object result = data[--size];System.out.println(Thread.currentThread().getName()+"消费了"+result);this.notifyAll();return result;}}//生产者class Product implements Runnable{Container container;public Product(Container container){this.container=container;}@Overridepublic void run() {for (int i = 1; i < 10; i++) {container.add("鸭子"+i);}}}//消费者class Consumer implements Runnable{Container container;public Consumer(Container container){this.container=container;}@Overridepublic void run() {for (int i = 1; i < 10; i++) {container.get();}}}class test{public static void main(String[] args) {Container container = new Container();Thread xfz1 = new Thread( new Consumer(container) );xfz1.start();Thread scz1 = new Thread( new Product( container ) );scz1.start();}}
信号灯法
*通过标志位区判断:如果标志位为真 等待 标志位为假通知另一个等待
//信号灯法class demo{public static void main(String[] args) {DT d = new DT();Gz g = new Gz(d);Yy y = new Yy(d);g.start();y.start();}}class Yy extends Thread{DT dt;public Yy(DT dt){this.dt=dt;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {this.dt.play("快乐大本营");}}}class Gz extends Thread{public Gz(DT dt){this.dt=dt;}DT dt;@Overridepublic void run() {for (int i = 0; i < 20; i++) {this.dt.watch();}}}class DT{String name;boolean flag = true;public synchronized void play(String name){while(!flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("演员表演了:"+name);this.name=name;this.notifyAll();this.flag=!flag;}public synchronized void watch(){while(flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("观众观看了:"+name);this.notifyAll();this.flag=!flag;}}
六.线程池
先讲实现Callable接口;call 是一个有返回值的方法 Callable<> 带泛型<br />
6.1 池:
是一种容器的概念,池化技术,都是容器技术。创建多个线程对象存入一个容器中,用就去池中获取,用完再放回去
因为线程也是宝贵的资源,频繁的创建销毁线程会存在内存开销。
Executor : 线程池的顶级接口
ExecutorService: Executor 的子接口,扩展了一些管理线程任务的方法。
ScheduledExecutorService: ExecutorService 的子接口 提供了延时执行任务的方法
6.2 如何创建线程池
Executor : 线程池的顶级接口
ExecutorService: Executor 的子接口,扩展了一些管理线程任务的方法。
ScheduledExecutorService: ExecutorService 的子接口 提供了延时执行任务的方法
corePoolSize:
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会停止
<br />Executors 线程池的工具类 ,可以创建出线程池实例 多个S就是 工具类 没有就是接口<br />ExecutorService: Executor 的子接口<br /> 方法:<br />void execute(Runnable command); 执行命令 无返回值 一般用于执行Runnable接口<br /><T>future<T> submit (Callable<T> task) ; 执行命令 有返回值 一般用于执行Callable接口<br />void shutdown(); 关闭连接 <br />提交任务到线程池,Runnable 类型的任务,Callable 类型的任务 <br />Future 保存异步运算的结果,它提供了一个get()用于返回真正的异步计算结果,<br /> 但是调用get()方法会造成调用线程阻塞,直到获得结果为止。<br /> <br />//1 单线程池<br /> ExecutorService es = Executors.newSingleThreadExecutor();
//2 固定线程池
ExecutorService es2 = Executors.newFixedThreadPool(10);
//3 可变数量的线程池
ExecutorService es3 = Executors.newCachedThreadPool();
//4 创建定时任务线程池
ScheduledExecutorService se4 = Executors.newScheduledThreadPool(10);
6.3.提交任务到线程池
6.3.1 Runnable 类型的任务
//关注运行没有结果的任务class WashFeet implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"洗脚...");try {Thread.sleep(3000);//模拟一个延时} catch (InterruptedException e) {e.printStackTrace();}}}
方法名为 run() 没有返回值 不可抛出异常
6.3.2 Callable 类型的任务
class Numcount implements Callable<Integer>{@Overridepublic Integer call() throws Exception {int sum=0;for (int i = 0; i <= 100; i++) {sum+=i;}System.out.println(Thread.currentThread().getName() +"sum:"+sum);return sum;}}class demo2{public static void main(String[] args) {Numcount num = new Numcount();ExecutorService es = Executors.newSingleThreadExecutor();Future<Integer> receive = es.submit(num);Integer result = null;try {result = receive.get();} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}System.out.println(result);}}
特点: 方法名为 call() 有返回值 可抛出异常
6.3.3 Future 异步结果
Future 保存异步运算的结果,它提供了一个get()用于返回真正的异步计算结果,但是调用get()方法会造成调用线程阻塞,直到获得结果为止。
Future<Integer> result = es2.submit( new NumberCount() );Integer sum = result.get(); //阻塞调用语句的线程System.out.println(sum);
6.4 关闭线程池
shutdown() 方法,调用后线程池可拒绝接收新的任务,带全部现有任务执行完毕后关闭线程池。<br />
6.5 统计多个任务运行的总时间
使用 shutdown()+isTerminated() 循环判断<br /> CountDownLatch 同步辅助工具类 , 构造一个任务总数,每个任务结束就减一,直到为0,把调用 await() 的线程唤醒。
七.并发集合 (JUC 集合)

CopyOnWriteArrayList : 底层使用 ReentrantLock实现锁,并且修改数据,拷贝一个新的数字操作。
CopyOnWriteArraySet : 底层套娃使用 CopyOnWriteArrayList ,添加前通过addIfAbsent(e) 判断元素是否已经存在,如果存在着不添加。
ConcurrentHashMap : 底层使用(1.8早期分段锁 后期使用CAS 无锁算法)
ConcurrentLinkedQueue : 并发安全的 队列,链表实现
ArrayBlockingQueue : 阻塞队列,基于数组实现 ,也是有有界队列
LinkedBlockingQueue: 阻塞队列,基于链表实现, 是无界队列
补充:
volatile 【面试题】
关键字 用于修饰变量 , 它作用是,可以将线程工作内存中的数据,立即同步到主存,以保证其他线程池立即可见。简单的说就是解决内存可见性问题。但是不能保证原子性问题。
JAVA (JMM)java内存模型:
JUC 中除有线程池 锁 同步工具, 还提供了一大堆 原子操作类。
AtomicInger AtomicLong……. 类它们的底层使用cas算法保证线程安全问题。
附图
JVM内存模型:

案例:
用sychorized 不交互打印
class Windows implements Runnable{public static void main(String[] args){Thread t1 =new Thread(new Windows("a"));t1.start();Thread t2 =new Thread(new Windows("daa"));t2.start();}private String data;public Windows(String data ){this.data=data;}//不交互打印 datapublic void run(){synchronized (System.in) {for (int i = 0; i < 100; i++) {System.out.println(data);}}}}
作业:计算1-100000之间的素数有哪些
class Prime implements Runnable{@Overridepublic void run() {int a = 0;for (int i = 2; i < 100000; i++) {boolean b = true;for (int j =2; j < i ; j++) {if(i%j==0){b = false;break;}}if(b){a++;}}System.out.println("2~100000之间的素数有"+a+"个");}}
计算1-100000之间的素数有哪些
class Prime2 implements Runnable{@Overridepublic void run() {int n = 0;//用来计数for (int i =100000; i <= 200000; i++) {if (isPrimeNumber(i))n++;}System.out.println("100000~200000之间的素数"+n+"个");}static Boolean isPrimeNumber(long num) {//判断是否为素数//判断一个数是否为素数if (num == 2) return true;//2特殊处理if (num < 2 || num % 2 == 0) return false;//识别小于2的数和偶数for (int i = 3; i <= Math.sqrt(num); i += 2) {if (num % i == 0) {//识别被奇数整除return false;}}return true;}}

