• JAVA多线程大总结篇


    多线程

    一:java语言中,实现线程的两种方式

      java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。

    第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。
        // 定义线程类
        public class MyThread extends Thread{
            public void run(){
            
            }
        }
        // 创建线程对象
        MyThread t = new MyThread();
        // 启动线程。
        t.start();
    
    第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。
        // 定义一个可运行的类
        public class MyRunnable implements Runnable {
            public void run(){
            
            }
        }
        // 创建线程对象
        Thread t = new Thread(new MyRunnable());
        // 启动线程
        t.start();
    
    注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承
    其它的类,更灵活。
    第三种方式: 实现Callable接口,重写call()方法,-->juc包下的           可以抛出异常,可以有返回值 (知道就行)

    二:关于线程对象的生命周期

      新生状态 : new的时候,线程处于新生状态

    ​  就绪状态 : start()线程就会进入到就绪状态,线程就会进入到就绪队列进行等 待,等待cpu的调度

    ​  运行状态 : cpu把资源分配给这个线程,线程才能进入运行状态

    ​  阻塞状态 : sleep()可以让线程进入阻塞

    ​  终止状态 : 线程结束

    注意:

    一个线程一旦进入阻塞状态,阻塞接触 无法直接恢复到运行状态,会直接进入就绪状态

    如果一个线程一旦终止,无法恢复,重新创建也不是刚刚的线程

    进入终止状态的情况

      1. stop destroy() 不推荐使用

      2. 通过标识判断–推荐

      3. 线程正常执行完毕

    进入就绪状态的情况

    1. start()

    2. 阻塞解除

    3. 线程切换

    4. yield() 礼让线程 让出CPU资源 , 原线程直接 进入就绪 状态

    /*
    
    - 礼让线程 yield  --> 高风亮节
    
    - 让出cpu资源 , 原线程直接 进入就绪 状态
      */
      public class YieldDemo04 implements Runnable{
      public static void main(String[] args) {
          YieldDemo04 y = new YieldDemo04();
          new Thread(y,"A").start();
          new Thread(y,"B").start();
      }
    
      @Override
      public void run() {
          System.out.println(Thread.currentThread().getName()+"开始----------------------");
          Thread.yield();
          System.out.println(Thread.currentThread().getName()+"结束----------------------");
      }
    
    }

    进入阻塞状态的情况

    1. sleep()

    2. wait()

    3. join()

    /*
    
    join()  插队线程|加入
    
    插队之前先就绪
     */
    public class JoinDemo06 implements Runnable{
    public static void main(String[] args) {
        Thread th = new Thread(new JoinDemo06());
        
    
    for(int i = 1;i<=10;i++) {
        System.out.println("main线程执行--------------"+i);
        if(i==5){
            th.start();  //就绪插队
            try {
                th.join(1000);   //th线程插队到当前主线程中,主线程等待th线程结束,才能继续执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            } //插队
        }
    }
    
    }
    
    @Override
    public void run() {
        System.out.println("--------------------A线程开始执行--------------------");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("--------------------A线程结束执行--------------------");
    }
    
    
    
    }

    sleep(ms) : 睡眠

    在sleep休眠时间过程当中,当前会让出CPU的资源, 当休眠时间解除,恢复到就绪,可供CPU选择抱着资源睡觉,—> 对象资源(对象的锁),不会释放锁

      1. 网络延迟

      2. 放大问题的可能性

    示例:

    /*
    
    根据标识做判断,控制线程的结束
    
    龟兔赛跑
    
    兔子每跑10步休息 , 乌龟不休息,  有人赢了就结束, 跑100步就赢了
     */
    public class Race02 implements Runnable{
    //标识  记录赢了人的姓名
    private String winner;
    
    @Override
    public void run() {
        //i控制步数
        for(int i =1;i<=100;i++) {
            //  兔子    10步
            System.out.println(Thread.currentThread().getName()+"正在跑第"+i+"步");
            if("兔子".equals(Thread.currentThread().getName()) && i%10==0) {
                try {
                    Thread.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //判断是否要结束方法
            if(!checkOver(i)) {
                break;
            }
        }
    }
    
    //判断是否结束线程  返回值false结束线程  true继续跑步
    public boolean checkOver(int steps) {
        //是否已经有人赢了, 结束
        if(winner != null) {
            return false;
        }
        //没有人赢了,判断我自己赢没赢
        if(steps == 100) {
            winner = Thread.currentThread().getName();
            return false;
        }
        return true;
    }
    
    public static void main(String[] args) {
        Race02 race = new Race02();
        new Thread(race,"兔子").start();
        new Thread(race,"乌龟").start();
    }
    }

    三.线程的调度

      1.常见的线程调度模型有哪些?

        抢占式调度模型:
          那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
          java采用的就是抢占式调度模型。

        均分式调度模型:
          平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
          平均分配,一切平等。有一些编程语言,线程调度模型采用的是这种方式。

      2.java中提供了哪些方法是和线程调度有关系的呢?

    实例方法:
                void setPriority(int newPriority) 设置线程的优先级
                int getPriority() 获取线程优先级
                最低优先级1
                默认优先级是5
                最高优先级10
                优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。)
            
    
    静态方法:
    ​        static void yield()  让位方法
    ​        暂停当前正在执行的线程对象,并执行其他线程
    ​        yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。
    ​        yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。
    ​        注意:在回到就绪之后,有可能还会再次抢到。
    ​    
    实例方法:
    ​        void join()  
    ​        合并线程
    ​        class MyThread1 extends Thread {
    ​            public void doSome(){
    ​                MyThread2 t = new MyThread2();
    ​                t.join(); // 当前线程进入阻塞,t线程执行,直到t线                程结束。当前线程才可以继续。
    ​            }
    ​    }
    
    ​        class MyThread2 extends Thread{
    ​            
    }

    四.关于多线程并发环境下,数据的安全问题

      以后在开发中,我们的项目都是运行在服务器当中,而服务器已经将线程的定义,线程对象的创建,线程的启动等,都已经实现完了。这些代码我们都不需要编写。

    ​   最重要的是:你要知道,你编写的程序需要放到一个多线程的环境下运行,你更需要关注的是这些数据在多线程并发的环境下是否是安全的。

    ​  

      1.线程同步机制与异步机制:

    异步编程模型:
            线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,
            谁也不需要等谁,这种编程模型叫做:异步编程模型。
            其实就是:多线程并发(效率较高。)
    
            异步就是并发。
    
    同步编程模型:
            线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行
            结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,
            两个线程之间发生了等待关系,这就是同步编程模型。
            效率较低。线程排队执行。
    
            同步就是排队。

      2.Java中有三大变量?【重要的内容。】

    实例变量:在堆中。
    
    静态变量:在方法区。
    
    局部变量:在栈中。
    
    以上三大变量中:
        局部变量永远都不会存在线程安全问题。
        因为局部变量不共享。(一个线程一个栈。)
        局部变量在栈中。所以局部变量永远都不会共享。
    
        实例变量在堆中,堆只有1个。
        静态变量在方法区中,方法区只有1个。
        堆和方法区都是多线程共享的,所以可能存在线程安全问题。
    
        局部变量+常量:不会有线程安全问题。
        成员变量:可能会有线程安全问题。

      如果使用局部变量的话:建议使用:StringBuilder。
      因为局部变量不存在线程安全问题。选择StringBuilder。
      StringBuffer效率比较低。

    ​   ArrayList是非线程安全的。
    ​   Vector是线程安全的。
    ​   HashMap HashSet是非线程安全的。
    ​   Hashtable是线程安全的。

    如何控制线程安全问题:

      synchronized 关键字 控制线程安全–> 控制多个线程排队执行

    用法:
    
      1.同步方法 : 简单 效率较低
    
    ​ 成员方法
    
    ​ 静态方法
    
      2.同步块
    
    ​   synchronized(this|类.class| 资源(成员属性)){ --> this 类 资源(成员属性)要被你同步控制的代码区域–>有可能 会出现问题的代码区域}double check 双重检查提高效率
    
    注意:
    
    同步一定要同步不变的东西,会变的锁不住,对象地址肯定不会变
    
    同步的代码范围过大,效率低,代码范围小,锁不住
    
    静态方法时属于 类的,同步静态方法,相当于同步类.class -->(类的class对象每一个类只有一个,不用你创建,在类加载到内存中时就已经存在)
    public class SynDemo01 {
        public static void main(String[] args) {
            new Thread(()->{
                System.out.println(SingleTon.newInstance());
            }).start();
            new Thread(()->{
                System.out.println(SingleTon.newInstance());
            }).start();
            new Thread(()->{
                System.out.println(SingleTon.newInstance());
            }).start();
        }
    }
    
    //懒汉式
    class SingleTon{
        //2私有的静态的该类的引用 
        private static SingleTon singleTon;
        
    
    //1.私有的构造器
    private SingleTon(){}
    
    //A B C
    //3.公共的静态的访问方式,返回该类实例
    public static SingleTon newInstance() {
        System.out.println("-----------------");
        
        if(singleTon == null) {
            System.out.println("-----满足打印的话-----------");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //双重检查   double check  -->提高效率
            synchronized (SingleTon.class) { //同步类
                if(singleTon == null) {
                    singleTon = new SingleTon();
                }
            }
        }
        return singleTon;
    }
    public static SingleTon newInstance() {
            System.out.println("-----------------");
            
            synchronized (SingleTon.class) { //同步类
                if(singleTon == null) {
                    System.out.println("-----满足打印的话-----------");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                        singleTon = new SingleTon();
                }
            }
                        return singleTon;
        }
    //同步方法
        public static synchronized SingleTon newInstance() {
            if(singleTon == null) {
                try {
                        Thread.sleep(1000);
                } catch (InterruptedException e) {
                        e.printStackTrace();
                }
                    singleTon = new SingleTon();
            }
                return singleTon;
        }

    借鉴自:https://blog.csdn.net/Yuan_Xiao_Mu/article/details/107291722

     更加全面的总结:https://blog.csdn.net/ChrisLu777/article/details/106587065

  • 相关阅读:
    [java] 怎么去掉小数点后面不需要的0
    [SoapUI] 在SoapUI script里获取Response(Json格式)某个节点值
    nacos启动不停打印日志[com.alibaba.nacos.client.naming.updater] INFO com.alibaba.nacos.client.naming:192
    sppringcloud应用启动 访问gateway无法path无法路由到目标应用,404
    springcloud项目启动gateway报错org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' that could not be found
    nacos 01
    微服务spring-cloud day2
    springcloud本地启动指定profile后错误If you are using the git profile, you need to set a Git URI in your configuration.
    微服务spring-cloud day1
    金融云部署sofaboot应用指定了项目路径健康检查无法通过,windows格式/unix格式/mac格式的坑
  • 原文地址:https://www.cnblogs.com/instead-everyone/p/13619153.html
Copyright © 2020-2023  润新知