多线程
多线程
多个线程并发
1 早期单核心单线程
2 双核心四线程
3 四核心八线程
4 八核心八线程
并行同时执行
并发交替执行
进程 正在运行的程序
买电脑
内存 32G
硬盘 1T固态
CUP 英特尔 amd×
显卡:集成显卡 独立
一实现多线程的三种方式
1 继承Thread类 重写run()方法 ( 里面写线程开启后执行的方法)
2 实现Runnable接口 重写run()方法 是Threa类实现的接口
3 实现Callable接口,重写call() , 具备返回。
FutureTask类 Runnable接口的孙子
FutureTask类中的get() 方法得到 call()的返回值 ,但要在线程执行结束后使用,否则死锁
二 Thread 类
上面的的三种方法,执行线程都需要Thread类来执行,因为有多态机制,使得有了上面的的三种方法。但都是作为参 数传递给它的构造。
下面介绍Thread类中的几个常用成员方法
String getName() //获取线程的名字
void setName(String name)//设置线程的名字
static Thread currentThread() //获取当前执行的线程对象
sleep (long time) //线程休眠毫秒
start() //开启线程
public final void setPriority(int newPriority)//设置线程优先级
public final int getPriority() //得到线程优先级
public final void setDaemon(boolean b)/设置守护线程 true设置为守护线程 普通线程结束后,守护线程也会消失。
第一种 继承Thread类 线程默认名称 Thread-编号 Thread的成员方法getName获得名字
setName起名字
构造方法起名字 需要 子类有参构造,super(参数),不调用默认的super()
package com.yang.thread; public class MyThread extends Thread{ public MyThread(String name) { super(name); } public MyThread() { } @Override public void run(){ for (int i = 0; i < 100; i++) { System.out.println(getName()+"线程呢个开启了"+i); } } }
package com.yang.thread; public class MyTest { public static void main(String[] args) { //创建一个线程对象 MyThread myThread=new MyThread("小小"); //开启一条线程 myThread.start(); //创建一个线程对象 MyThread myThread1=new MyThread("大大"); //开启第二条线程 myThread1.start(); } }
第二种 实现接口的方法实现线程的改名方法
Thread类中
跟Thread类没有继承关系得类 Thread.currentThread().getName();//获取运行线程的名称
Thread(Runnable实现类对象,线程名字)构造方法
Thread thread=new Thread(myRunnable,"黑羊羊");
Thread.currentThread().getName()
Thread类方法
1 static void sleep(long time);指定线程休眠 毫秒
@Override public void run(){ while(true){ if(num<=0){ break; //卖完了 }else { try{ Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); } } num--; System.out.println(Thread.currentThread().getName()+"还剩"+num+"票"); } }
2 线程调度模型
分时调度 轮流平均占用cup时间片
抢占式调度模型 优先级高的线程抢占CUP
java采用抢占式模型
setPriority(int )设置优先级 getPriority()获得优先级
3后台线程/守护线程
final void setDaemon(boolean b)/设置守护线程 true设置为守护线程 普通线程结束后,守护线程也会消失。
public class Demo { public static void main(String[] args) { MyThread1 myThread1=new MyThread1("将军"); MyThread2 myThread2=new MyThread2("士兵"); //设置士兵线程为守护线程 myThread2.setDaemon(true); myThread1.start(); myThread2.start(); } }
三 线程的安全
1原因
多个线程访问共享数据,执行线程方法中的每一行代码,都会失去CPU的执行权力!
2 解决方案 多条语句操作共享数据代码块锁起来
1 同步代码块
2 同步方法
3 Lock
1 同步代码块 自动开自动关
synchronized(任意对象){ //多个线程必须使用同一把锁
多条语句操作共享数据的代码
}
第一种继承Thread 数据要加静态,
第二种实现Runnable
package com.yang.ticket; public class Ticket implements Runnable{ public int num=100; private Object ticket=new Object(); @Override public void run(){ while(true){ try{ Thread.sleep(100); }catch (InterruptedException e){ e.printStackTrace(); } synchronized (ticket){ if(num<=0){ break; //卖完了 }else { } num--; System.out.println(Thread.currentThread().getName()+"还剩"+num+"票"); } } } }
同步方法 锁住方法中的所有代码
synchronized 返回值类型 方法名(){}
锁对象this
静态同步方法static synchronized 返回值类型 方法名(){}
锁对象 当前类名.class 当前类字节码对象
2 lock锁
void lock()获得锁
void unLock()开锁
ReentrantLock Lock接口的实现类。加锁解锁先创建该对象(ReentranLock)
package com.yang.Lock.MyLock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Ticket implements Runnable{ private int num=100; private ReentrantLock reentrantLock=new ReentrantLock(); @Override public void run() { while(true){ try { reentrantLock.lock(); if (num<=0){ break; }else{ Thread.sleep(100); num--; System.out.println(Thread.currentThread().getName()+"票还有"+num+"张"); } } catch (Exception e) { e.printStackTrace(); } finally { reentrantLock.unlock(); } } } }
死锁
多个线程相互持有对方执行所需要的资源,互不相让,相互等待。
等待唤醒机制最基本的实现方式
生产者和消费者 多线程协作模式
Object类中的
wait() 让线程处于等待状态 等待状态允许别的线程执行和sleep不一样
notify() 叫醒一个处于等待的线程
notifyAll() 叫醒所有处于这把锁上等待的线程。
必须由锁对象调用
public class Consumer extends Thread{ @Override public void run(){ while (Desk.num>0) { synchronized (Desk.student) { if(Desk.flag==false){ System.out.println(getName()+"又做了一个汉堡"); Desk.flag=true; Desk.student.notifyAll(); }else{ try { Desk.student.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
public class Productor extends Thread{ @Override public void run(){ while (Desk.num>0) { synchronized (Desk.student) { if(Desk.flag==true){ System.out.println(getName()+"我吃了一个汉堡还想吃"+(--Desk.num)); Desk.flag=false; Desk.student.notifyAll(); }else { try { Desk.student.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
public class Desk { public static boolean flag=false; public static int num=10; public static Student student=new Student(); }
public class MTest { public static void main(String[] args) { Consumer consumer=new Consumer(); Productor productor=new Productor(); consumer.setName("消费者"); productor.setName("生产者"); consumer.start(); productor.start(); } }
阻塞队列
阻塞队列实现等待唤醒机制,代码实现更简洁
public class Foodie extends Thread{ private final ArrayBlockingQueue<String> arrayBlockingQueue; public Foodie(ArrayBlockingQueue<String> arrayBlockingQueue) { this.arrayBlockingQueue = arrayBlockingQueue; } @Override public void run(){ while(true){ try { System.out.println("吃了一个"+arrayBlockingQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
public class Cooker extends Thread{ private ArrayBlockingQueue<String> arrayBlockingQueue; public Cooker(ArrayBlockingQueue<String> arrayBlockingQueue) { this.arrayBlockingQueue = arrayBlockingQueue; } @Override public void run(){ while(true){ try { arrayBlockingQueue.put("汉堡包"); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("厨师放了一个汉堡包"); } } }
public class MyTest { public static void main(String[] args) { ArrayBlockingQueue<String> arrayBlockingQueue=new ArrayBlockingQueue<>(1); Cooker cooker=new Cooker(arrayBlockingQueue); Foodie foodie=new Foodie(arrayBlockingQueue); cooker.start(); foodie.start(); } }
BlockingQueue:
put();将参数放入队列,如果放不进去堵塞
take() ;返回取到的元素,取不出来等待
ArrayBlockingQueue(阻塞队列最多能放几个容量) 底层数组,有界
LInkedBliokingQueue() 底层链表,无界(int 的最大值)
public class MyArrayBlockingQueue { public static void main(String[] args) throws InterruptedException { LinkedBlockingQueue<String> arrayBlockingQueue=new LinkedBlockingQueue<>(1); arrayBlockingQueue.put("汉堡包"); System.out.println(arrayBlockingQueue.take()); arrayBlockingQueue.take(); System.out.println("我结束了吗"); } }
阻塞队列和等待唤醒机制
线程处于运行状态(抢到CUP的执行权)和CPU产生关系,JVM没有定义线程的运行状态
线程池 存放经常用的线程
三种方式
1 创建一个默认的空池子 ,可以容纳 int 最大值创建使用Executors中的静态方法
ExecutorService控制线程池
Executors 帮助我们创建线程池对象
ExecutorService executorService=Executors.newCachedThreadPool(); //返回池子的控制者对象
新的线程任务,会查看线程池有没有空闲的线程对象,没有创建新的,有用线程池中的
2 submit 提交
3 shutdown 关闭线程池
executorService.shutdown()
public class MyExecutor { public static void main(String[] args) throws InterruptedException { //返回一个线程池的控制者对象 MyRunnable myRunnable=new MyRunnable(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.submit(()->{ System.out.println(Thread.currentThread().getName()+"线程在执行了"); }); Thread.sleep(100); executorService.submit(myRunnable); executorService.shutdown(); } }
2 创建一个指定最多线程数量的线程池
ExecutorService executorService=Executors.newFixedThreadPool(线程数量);
public class MynewFixedThreadPool { public static void main(String[] args) { //返回一个固定长度的线程池 ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.submit(()->{ System.out.println(Thread.currentThread().getName()+"线程池在执行"); }); executorService.submit(()->{ System.out.println(Thread.currentThread().getName()+"线程池在执行"); }); executorService.submit(()->{ System.out.println(Thread.currentThread().getName()+"线程池在执行"); }); executorService.shutdown(); } }
3 ThreadPoolExecutor
1正式员工数 不能小于0
2最大员工数 不能<0 >=核心数量
3临时员工空闲多久被辞退(值) 不能为null
4临时员工空闲多久被辞退(单位) 不能为null
5排队的客户
6 从哪里找人
7当排队人数过多,超出顾客下次再来
package com.yang.threadpool; import java.util.concurrent.*; public class MyThreadPool { public static void main(String[] args) { ThreadPoolExecutor pool=new ThreadPoolExecutor(2, //参数一 核心线程数量 5, //最大线程数量 2, //空闲线程最大存活时间 TimeUnit.DAYS, //时间单位--TimeUnit定义了时间单位 new ArrayBlockingQueue<>(10), //任务队列--让任务排队,有线程空闲了在执行 Executors.defaultThreadFactory(), //创建线程工厂 new ThreadPoolExecutor.AbortPolicy() //任务的拒绝策略 超过排队数量+最大线程数量该怎末办 ); /* 特点 1 如果提交任务书小于等于核心线程的数量,都会创建核心线程 2 如果超过核心线程的数量,并且小于等于核心线程数量+排队数量,不会创建临时线程继续用核心线程。 3 超过核心线程数量,并且大于核心线程数量+排队数量开始创建临时工。
4 任务数量超过,最大线程数量+排队数量,开始执行默认的拒绝策略
*/
}
}
拒绝策略
AbortPlicy() 拒绝服务,抛出异常
DiscardPolicy() 不抛出异常直接抛弃
DiscardOldestPolicy()抛弃等待最久的任务,然后把当前任务加队列
CallerRunPolicy() 不采用线程池执行,采用其他线程执行
共享数据拷贝到线程内存中,如果线程一直使用(线程执行太快发现的几率小,慢了发现新的数据的几率大)这个数据,不会去获取最新的共享数据。
volatile 关键字解决 易变的 不稳定的 (但不能保证原子性)强制线程使用数据时,都会区查看共享数据的值
将共享数在线程的变量副本中临时存储,当另一个线程更改共享数据时,这个线程仍然使用之前的变量副本数据,也会去查看新的共享数据,但是无法控制
public static volatile int money=100000;
易变的数据
public class Boy implements Runnable{ @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Money.money=1; } }
public class Girle implements Runnable{ @Override public void run() { while (Money.money==100000){ } System.out.println("不是十万了"); } }
public class Money { public static volatile int money=100000; }
public class MyTest { public static void main(String[] args) { Boy boy=new Boy(); Thread thread=new Thread(boy); thread.setName("小皮"); Girle girle=new Girle(); Thread thread1=new Thread(girle); thread1.setName("小傻"); thread.start(); thread1.start(); }}
synchronized 同步代码块解决 强制我们去看共享数据的最新值
1 线程获得锁对象
2 清空自己变量副本中保留的值
3 拷贝共享变量最新值到变量副本
4执行代码
5 先修改变量副本中的值在赋值给共享数据
6 释放锁
原子性
多个操作要么同时成功,要么同时失败,多个操作是一个不可分割的整体(事务)
a++不具有原子性 a+1执行了,但在要赋值给共享数据的时候,cup执行权被其他线程抢走了,volatile不能保证原子性。
可以用synchronized
原子类类 Atomic+基本数据类型包装类
public static void main(String[] args) { AtomicInteger atomicInteger=new AtomicInteger(); AtomicInteger atomicInteger1=new AtomicInteger(10); System.out.println(atomicInteger); System.out.println(atomicInteger1); int i = atomicInteger1.incrementAndGet(); System.out.println("先增加在返回"+i); int andIncrement = atomicInteger1.getAndIncrement(); System.out.println("先返回再增加"+andIncrement); int i1 = atomicInteger1.addAndGet(10); System.out.println("先增加在返回"+i1); int andSet = atomicInteger1.getAndSet(866); System.out.println("得到旧值返回新值"+andSet); }
创建对象,通过方法操作数据
private int count=0; @Override public void run() { for (int i = 0; i < 100; i++) { //count++不是原子操作 //1 count获取共享数据的值,到栈变量副本中 //2将本线程栈变量副本的值+1 //3 将变量副本的值更新到共享数据 //4其中任何一部都可能丢失cup的执行权 count++; System.out.println("已经送了"+count+"个鸡蛋"); } }
解决
public class MyRunnable implements Runnable{ private AtomicInteger atomicInteger=new AtomicInteger(0); @Override public void run() { for (int i = 0; i < 100; i++) { //count++不是原子操作 //1 count获取共享数据的值,到栈变量副本中 //2将本线程栈变量副本的值+1 //3 将变量副本的值更新到共享数据 //4其中任何一部都可能丢失cup的执行权 int count=atomicInteger.incrementAndGet(); System.out.println("已经送了"+count+"个鸡蛋"); } } }
public class MyTest { public static void main(String[] args) { MyRunnable myRunnable=new MyRunnable(); for (int i = 0; i < 100; i++) { new Thread(myRunnable).start(); } } }
AtomicInteger原理 自旋锁 +CAS算法
CAS算法
增加了保存旧值的内存,修改时若旧值,和内存值(共享数据)比较,一样没被修改过,写入,不一样,修改失败。再重新获取最新值。 (这个重新获取)重新获取过程叫做自旋
例如 AtomIcIntager
乐观锁:持有一个乐观的态度,不会通过(锁机制)上锁,操作过程通过判断,来判断是否有其他线程影响了数据,影响了数据,则终止本次操作,在重新获取数据进行操作
悲观锁:认为在本线程操作过程中,会有其他线程来影响,直接加锁(同步代码块,同步方法,Lock)
并发工具类
HashMap是线程不安全的(多线程下不安全)为保证数据的安全性我们使用hashtable(Map的实现类)里面的方法采用同步方法,但是效率低下(采用有线程调用时悲观锁直接锁起整张表)
public class MyHashMap { public static void main(String[] args) { HashMap<String,String> hashMap=new HashMap<>(); Thread t1=new Thread(()->{ for (int i = 0; i < 25; i++) { hashMap.put(i+"",i+""); ; } }); Thread t2=new Thread(()->{ for (int i = 25; i < 50; i++) { hashMap.put(i+"",i+""); } }); t1.start(); t2.start(); for (int i = 0; i < 51; i++) { System.out.println(hashMap.get(i + "")); } } }
Hashtable
public class MyHashMap { public static void main(String[] args) throws InterruptedException { Hashtable<String,String> hashMap=new Hashtable<>(); Thread t1=new Thread(()->{ for (int i = 0; i < 25; i++) { hashMap.put(i+"",i+""); ; } }); Thread t2=new Thread(()->{ for (int i = 25; i < 51; i++) { hashMap.put(i+"",i+""); } }); t1.start(); t2.start(); Thread.sleep(1000); for (int i = 0; i < 51; i++) { System.out.println(hashMap.get(i + "")); } } }
效率低是因为锁的颗粒度比较大(锁住整张hash表)
ConcurrentHashMap 安全,且兼顾效率 Map的实现类
Segment[] 存放地址值,该地址值是第一次计算的哈希值相同数据组成的哈希表的地址
JDK1.8做了优化 底层;哈希表 (数组,红黑树,链表)
安全机制;CAS +synchronized同步代码块
大数组可以扩容 l en*0.75 扩容
链表数据到达8,自动转为红黑树结构
多线程场景下,双列集合。优先使用ConcurrentHashMap 单列集合 Colletions (api中写的很清楚) 例如 Collections.SynchronizedList(实现类对象)返回一个线程安全的集合
(倒计数的门)
CountDownLatch 让某一条线程等待其他先后曾执行完毕后调用
妈妈等着五个孩子吃完,检查作业 6个线程 等待线程5个
countdown 倒计时
构造方法
public CountDownLatch(需要等待线程的数量)
countDownLatch.awite()
countDownLatch.countDown()
Semaphore 信号 管理员 管理同时执行多少条线程(一个路口可以通过几辆车,有通行证的车可以通过)
没有空参构造,参数允许几条线程运行(几张通行证)
semaphore.acquire(获得)(获得通行证)
semaphore.realse(释放) 释放通行证