多线程
多线程
多个线程并发
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(释放) 释放通行证

浙公网安备 33010602011771号