多线程

多线程
多个线程并发
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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
        }
    }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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实现类对象,线程名字)构造方法

 

1
Thread thread=new Thread(myRunnable,"黑羊羊");

  

1
Thread.currentThread().getName()

  

 

 

Thread类方法
1 static void sleep(long time);指定线程休眠 毫秒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void run(){
    while(true){
        if(num<=0){
            break;
            //卖完了
        }else {
            try{
                <strong>Thread.sleep(100);</strong>
            }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设置为守护线程 普通线程结束后,守护线程也会消失。

 

1
2
3
4
5
6
7
8
9
10
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

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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() 叫醒所有处于这把锁上等待的线程。

必须由锁对象调用

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
                    }
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
                    }
                }
            }
        }
    }
}
1
2
3
4
5
6
public class Desk {
    public static boolean flag=false;
    public static int num=10;
    public static Student student=new Student();
 
}
1
2
3
4
5
6
7
8
9
10
11
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();
 
    }
}

  

阻塞队列

阻塞队列实现等待唤醒机制,代码实现更简洁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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();
            }
 
 
        }
    }
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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("厨师放了一个汉堡包");
        }
    }
}

  

1
2
3
4
5
6
7
8
9
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 的最大值)

 

1
2
3
4
5
6
7
8
9
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()

1
2
3
4
5
6
7
8
9
10
11
12
13
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(线程数量);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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当排队人数过多,超出顾客下次再来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 超过核心线程数量,并且大于核心线程数量+排队数量开始创建临时工。 <br>      4 任务数量超过,最大线程数量+排队数量,开始执行默认的拒绝策略 <br>    */ <br>    }<br> }

  

拒绝策略 

AbortPlicy() 拒绝服务,抛出异常

DiscardPolicy() 不抛出异常直接抛弃

DiscardOldestPolicy()抛弃等待最久的任务,然后把当前任务加队列

CallerRunPolicy() 不采用线程池执行,采用其他线程执行

 

 

共享数据拷贝到线程内存中,如果线程一直使用(线程执行太快发现的几率小,慢了发现新的数据的几率大)这个数据,不会去获取最新的共享数据。

  volatile 关键字解决  易变的 不稳定的 (但不能保证原子性)强制线程使用数据时,都会区查看共享数据的值

将共享数在线程的变量副本中临时存储,当另一个线程更改共享数据时,这个线程仍然使用之前的变量副本数据,也会去查看新的共享数据,但是无法控制

 

1
public static volatile int money=100000;                         
1
易变的数据
1
2
3
4
5
6
7
8
9
10
11
12
public class Boy implements Runnable{
 
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Money.money=1;
    }
}

  

1
2
3
4
5
6
7
8
9
public class Girle implements Runnable{
    @Override
    public void run() {
        while (Money.money==100000){
 
        }
        System.out.println("不是十万了");
    }
}

  

1
2
3
public class Money {
    public static volatile int money=100000;
}

  

1
2
3
4
5
6
7
8
9
10
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+基本数据类型包装类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
 
   }

  

     创建对象,通过方法操作数据

1
2
3
4
5
6
7
8
9
10
11
12
13
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+"个鸡蛋");
     }
 }

 

 

 解决

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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+"个鸡蛋");
        }
    }
}

  

 

1
2
3
4
5
6
7
8
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的实现类)里面的方法采用同步方法,但是效率低下(采用有线程调用时悲观锁直接锁起整张表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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(释放)  释放通行证

 

posted @   互联.王  阅读(40)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示