多线程

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

 

posted @ 2021-10-20 12:36  互联.王  阅读(37)  评论(0编辑  收藏  举报