JAVA多线程快速入门

  • 01.什么是多线程
    • 概述
      • 线程
        • 线程是操作系统能够进行运算调度的最小单位 它被包含在进程之中,是进程中的实际运作单位
        • 简单理解
          • 应用软件中互相独立,可以同时运行的功能
      • 进程
        • 进程是程序的基本执行实体/系统分配资源的基本单位
    • 作用
      • 充分利用cpu 提高效率
    • 应用场景
      • 软件中的耗时操作
        • 拷贝\迁移大文件
        • 加载大量的资源文件
      • 所有聊天软件/所有后台服务器/多个事情同时运行就需要用到多线程
  • 02. 并发和并行
    • 并发
      • 概述
        • 在同一时刻,有多个指令在单个cpu上交替执行
    • 并行
      • 概述
        • 在同一时刻,有多个指令在多个cpu上交替执行

  • 03. 多线程的实现方式
    • 1.继承Thread类的方式进行实现
    • 2 . 实现Runnable接口的方式进行实现
    • 3 . 利用Callable接口和Future接口方式实现
  • 继承Thread类的方式代码实现
  • public class MyThread extends Thread{
        //程序内容
        @Override
        public void run(){
            for (int i = 0;i<100;i++){
                System.out.println(getName()+"HelloWord");
            }
        }
    }
    
    public class ThreadDemo {
        public static void main(String[] args) {
            /*多线程第一种启动方式
            1.自己定义一个类继承Thread类
            2.重写run方法
            3.创建子类的对象,启动线程
             */
            MyThread t1 = new MyThread();
            MyThread t2 = new MyThread();
            t1.setName("线程一");
            t2.setName("线程二");
            //开启线程
            t1.start();
            t2.start();
        }
    }

    实现Runnable接口的方式代码实现

  • public class MyRun implements Runnable{
        @Override
        public void run() {
            for (int i=0;i<100;i++){
               /* Thread t = Thread.currentThread();
                System.out.println(t.getName()+"HelloWord");*/
                System.out.println(Thread.currentThread().getName()+"HelloWord!!!");
            }
        }
    }
    public class ThreadDemo {
        public static void main(String[] args) {
            /*
            多线程第二种启动方式
            1.自己定义一个类实现Runnable接口
            2.重写run方法,将此线程要执行的操作声明在run方法中
            3.创建Runnable接口的实现类对象
            4.将此Runnable接口的实现类对象作为参数传递到Thread类的构造器中,创建Thread类的对象,开启线程*/
    
          //创建runnable接口的实现类对象
            MyRun mr = new MyRun();
            //创建Thread类的对象
            Thread t1= new Thread(mr);
            Thread t2 = new Thread(mr);
            //线程赋值
            t1.setName("线程一");
            t2.setName("线程二");
            //启动
            t1.start();
            t2.start();
        }
    }

    利用Callable接口和Future接口方式代码实现

  • public class MyCallable implements Callable<Integer> {
        @Override
        public Integer call() throws Exception {
            //  1到100之间的和
            int sum = 0;
            for (int i=1;i<=100;i++){
                sum +=i;
            }
            return sum;
        }
    }

测试类 

public class ThreadDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*多线程的第三种实现方式
        特点:可以获取到多线程运行的结果

        1.  创建一个类Callable实现callable接口
        2.  重写call方法(有返回值,表示多线程运行的结果)
        3.  创建MyCallable对象(表示多线程要执行的任务)
        4.  创建FutureTask的对象(表示对Callable对象包装,可以获取到Callable对象执行后的结果)
        5.  创建Thread对象,构造方法中传入FutureTask对象  (开始线程)
        * */
        
        //创建MyCallable对象
        MyCallable mc = new MyCallable();
        //创建FutureTask对象
        FutureTask<Integer> ft = new FutureTask<>(mc);
        //创建Thread对象
        Thread t =new Thread(ft);
        //开启线程
        t.start();
        //获取线程运行结果
        Integer integer = ft.get();
        System.out.println(integer);
    }
}


  • 04 常见的成员方法
    • string  getName()                  返回此线程的名称
      void   setName(String name)        设置此线程的名称(构造方法也可以设置名字)
      static Thread currentThread()      获取当前正在执行的线程对象
      static void sleep(long time)       rang线程休眠,单位毫秒
      代码实现
    • public class MyThread extends Thread{
          public MyThread(String name) {
              super(name);
          }
          @Override
          public void run(){
              for (int i =0;i<100;i++){
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
                  System.out.println(getName()+"@"+"HelloWord!!!");
              }
          }
      }

      测试类

    • public class ThreadDemo {
          public static void main(String[] args) throws InterruptedException {
              /*
                  string  getName()                           返回此线程的名称
                  void   setName(String name)        设置此线程的名称(构造方法也可以设置名字)
              细节:
                  1.线程名字默认是Thread-X(x是序号,从0开始)
                  2.如果要设置线程名字,可以在创建线程对象时,调用setName()方法,也可以用构造方法设置
      
                  static Thread currentThread()  获取当前正在执行的线程对象
                      细节:
                              当JVM虚拟机启动之后,会自动的启动多条线程
                              其中有一条叫main线程
                              它的作用是调用main方法,并执行main方法中的代码
                              在以前我们所写的代码,都是在main方法中执行的
      
      
                  static void sleep(long time)       rang线程休眠,单位毫秒
                      细节:
                                  1.那条线程执行到这个方法,就停留对应时间
                                  2.时间到期之后,线程继续执行下面代码
                                       */
      
              MyThread t1 = new MyThread("线程一");
              MyThread t2 = new MyThread("线程二");
              //启动线程
              t1.start();
              t2.start();
      
              //Thread t = Thread.currentThread();
              //System.out.println(t.getName());  main
      
              /*System.out.println("线程一");
               Thread.sleep(1000);
              System.out.println("线程二");*/
          }
      }
      setPriority(int newPriority)        设置线程优先级
      final int getPriority()             获取线程的优先级
      代码实现
    • public class MyRunnable implements Runnable{
          @Override
          public void run() {
              for (int i = 0; i < 100; i++) {
                  System.out.println(Thread.currentThread().getName()+"~~~~~~"+i);
              }
          }
      }

      测试类

      • public class ThreadDemo {
            public static void main(String[] args) {
              /*setPriority(int newPriority)        设置线程优先级
                final int getPriority()                   获取线程的优先级
                */
                MyRunnable mr =new MyRunnable();
                Thread t1 = new Thread(mr,"线程一");
                Thread t2 = new Thread(mr,"线程二");
        
                t1.setPriority(1);
                t2.setPriority(Thread.MAX_PRIORITY);
                t1.start();
                t2.start();
        //        System.out.println(Thread.currentThread().getPriority());
            }
        }
         final void setDaemon(boolean on)    设置守护线程
         boolean isDaemon()                  判断是否为守护线程
        代码实现
      • 类一
      • public class MyThread1 extends Thread{
            @Override
            public void run(){
                for (int i = 0 ;i<10;i++){
                    System.out.println(Thread.currentThread().getName() + "~~~" + i);
                }
            }
        }

        类二

      • public class MyThread2 extends Thread{
            @Override
            public void run(){
                for (int i = 0 ;i<100;i++){
                    System.out.println(Thread.currentThread().getName() + "~~~" + i);
                }
            }
        }

        测试类

        public class ThreadDemo {
            public static void main(String[] args) {
                /*  final void setDaemon(boolean on)    设置守护线程
                    boolean isDaemon()    判断是否为守护线程
                    守护线程:当进程中的所有线程都是守护线程时,进程结束
                */
                // 创建线程对象
                MyThread1 t1 = new MyThread1();
                MyThread2 t2 = new MyThread2();
                // 设置线程名称
                t1.setName("女神");
                t2.setName("备胎");
                // 设置备胎线程为守护线程
                t2.setDaemon(true);
                //开启
                t1.start();
                t2.start();
            }
        }
        public static void yield()       礼让线程/出让线程
        代码实现
      • public class MyThread extends Thread{
            @Override
            public void run(){
                for (int i = 0; i < 100; i++) {
                    System.out.println(getName()+"~"+i);
                    Thread.yield();
                }
            }
        }

        测试类

      • public class ThreadDemo {
            public static void main(String[] args) {
                // public static void yield()       礼让线程/出让线程
                MyThread myThread1 = new MyThread();
                MyThread myThread2=new MyThread();
                // 设置线程名称
                myThread1.setName("线程一");
                myThread2.setName("线程二");
                //开启线程
                myThread1.start();
                myThread2.start();
            }
        }
         public final void join()   插入线程/线程插队
         代码实现
      • public class MyThread extends Thread{
            @Override
            public void run(){
                for (int i = 0; i < 100; i++) {
                    System.out.println(getName()+"#"+i);
                }
            }
        }

        测试类

      • public class ThreadDemo {
            public static void main(String[] args) throws InterruptedException {
                // public final void join()   插入线程/线程插队
                MyThread t1 =new MyThread();
                //设置名字
                t1.setName("鸣人");
                //开始线程
                t1.start();
                //插入线程
                t1.join();
        
                for (int i = 0; i < 10; i++){
                    System.out.println("main");
                }
            }
        }
        

    • 抢占式调度
      • 默认5
    • 非抢占调度


  • 05 线程的生命周期
  • JVM没有运行状态,当线程抢夺到CPU的执行权,JVM会把当前线程交给操作系统管理
  • 这里只是便于理解

  • 06 线程安全的问题
    • 同步代码块
      • 把操作共享数据的代码锁起来
      • 格式
        • synchronized(锁){
        • 操作共享数据的代码
        • }
      • 特点
        • 锁默认打开,有一个线程进去了,锁自动关闭
        • 里面的代码全部执行完毕,线程出来
      • 小细节
        • 锁是唯一的
          • 如果产生两个锁,两个线程分别进不同的锁,锁就无意义了
        • synchronized不能写在循环的外面,会导致抢到cpu执行权的线程独立执行完循环后,其他线程才会进入
        • 可以用当前类的class
    • 关于锁的代码实现
    • //同步代码块
      public class MyThread extends Thread {
          static int ticket = 0;
      
          //锁对象  一定是唯一的  加static就行
      //  static    Object boj = new Object();
          @Override
          public void run() {
              while (true) {
                  //同步代码块
                  synchronized (MyThread.class) {
                      if (ticket < 100) {
                          try {
                              Thread.sleep(10);
                          } catch (InterruptedException e) {
                              throw new RuntimeException(e);
                          }
                          ticket++;
                          System.out.println(getName() + "卖票:" + ticket);
                      } else {
                          break;
                      }
                  }
              }
          }
      }

      测试类

    • public class ThreadDemo {
          public static void main(String[] args) throws ExecutionException, InterruptedException {
              //创建对象
              MyThread t1 = new MyThread();
              MyThread t2 = new MyThread();
              MyThread t3 = new MyThread();
              t1.setName("窗口一");
              t2.setName("窗口二");
              t3.setName("窗口三");
      
              t1.start();
              t2.start();
              t3.start();
          }
      }

    • 同步方法
      • 编辑
    • 同步方法代码实现
    • //抽取方法
      public class MyRun implements Runnable{
          int ticket =0;
          @Override
          public void run() {
              //循环
              while (true){
                  //同步代码块
                    if (method()) break;
                }
              }
          private synchronized boolean method() {
              //判断共享数据是否到了末尾,如果到了末尾
              if (ticket==100){
                  return true;
              }else {
                  //没到末尾,卖票
                  try {
                      Thread.sleep(10);
                  } catch (InterruptedException e) {
                      throw new RuntimeException(e);
                  }
                  ticket++;
                  System.out.println(Thread.currentThread().getName()+"卖票:"+ticket);
              }
              return false;
          }
      }

      测试类

    • public class ThreadDemo {
          public static void main(String[] args) {
              MyRun mr = new MyRun();
      
              Thread t1 = new Thread(mr,"窗口一");
              Thread t2 = new Thread(mr,"窗口二");
              Thread t3 = new Thread(mr,"窗口三");
      
              t1.start();
              t2.start();
              t3.start();
          }
      }

    • Lock锁

  • 手动开关锁代码实现
  • //手动开锁 关锁
    public class MyThread extends Thread {
        static int ticket = 0;
        static Lock lock = new ReentrantLock();
    
        @Override
        public void run() {
            while (true) {
                //同步代码块
                //synchronized (MyThread.class) {
                lock.lock();
                try {
                    if (ticket < 100) {
                        Thread.sleep(10);
                        ticket++;
                        System.out.println(getName() + "卖票:" + ticket);
                    } else {
                        break;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    测试类

  • public class ThreadDemo {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            //创建对象
            com.mtq.threadsafe01.MyThread t1 = new com.mtq.threadsafe01.MyThread();
            com.mtq.threadsafe01.MyThread t2 = new com.mtq.threadsafe01.MyThread();
            com.mtq.threadsafe01.MyThread t3 = new MyThread();
            //赋值
            t1.setName("窗口一");
            t2.setName("窗口二");
            t3.setName("窗口三");
    
            t1.start();
            t2.start();
            t3.start();
        }
    }
    

  • 07 死锁(只需理解即可,这是个错误)
  • 代码实现
  • public class MyThread extends Thread{
        static Object objA = new Object();
        static Object objB = new Object();
        @Override
        public void run(){
            //循环
            while (true){
                if ("线程A".equals(getName())){
                    synchronized (objA){
                        System.out.println("线程A获取到了objA的锁,准备拿objB锁");
                        synchronized (objB){
                            System.out.println("线程A获取到了objB的锁,顺利执行完一轮");
                        }
                    }
                }else if("线程B".equals(getName())){
                    if ("线程B".equals(getName())){
                        synchronized (objB){
                            System.out.println("线程B拿到了B锁,准备拿A锁");
                            synchronized (objA){
                                System.out.println("线程B拿到了A锁,顺利执行完一轮");
                            }
                        }
                    }
                }
            }
        }
    }
    

    测试类

  • public class ThreadDemo {
        public static void main(String[] args) {
            /*死锁*/
            MyThread t1 = new MyThread();
            MyThread t2 = new MyThread();
            t1.setName("线程A");
            t2.setName("线程B");
            t1.start();
            t2.start();
        }
    }
    

    输出结果

  • 会陷入一直不停止的状态


  • 08 生产者和消费者
    • 等待唤醒机制
      • 常见方法
    • 代码实现
    • 生产者类
    • public class Cook extends Thread {
          /*循环
            同步代码块
            判断共享数据是否到了末尾(到了末尾)
            判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)*/
          @Override
          public void run() {
              while (true) {
                  synchronized (Desk.lock) {
                      if (Desk.count == 0) {
                          break;
                      } else {
                          //判断是否有食物
                          if (Desk.foodFlag == 1) {
                              try {
                                  //有:等待
                                  Desk.lock.wait();
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                          } else {
                              //没有:制作
                              System.out.println(getName() + "制作了一个巨无霸十层汉堡王");
                              //把制作好的放于桌子
                              Desk.foodFlag = 1;
                              //唤醒消费者
                              Desk.lock.notifyAll();
                          }
                      }
                  }
              }
          }
      }
      

      消费者类

    • public class Foodie extends Thread {
          /*循环
          同步代码块
          判断共享数据是否到了末尾(到了末尾)
          判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)*/
      
          @Override
          public void run() {
              while (true) {
                  synchronized (Desk.lock) {
                      if (Desk.count == 0) {
                          break;
                      } else {
                          //判断是否有食物
                          if (Desk.foodFlag == 0) {
                              //没有继续等待
                              try {
                                  Desk.lock.wait();
                              } catch (InterruptedException e) {
                                  e.printStackTrace();
                              }
                          } else {
                              //每吃一份  -1
                              Desk.count--;
                              //有则吃
                              System.out.println("消费者" + getName() + "还能再吃" + Desk.count + "个巨无霸十层汉堡王");
                              //唤醒生产者
                              Desk.lock.notifyAll();
                              //桌子状态归为初始状态
                              Desk.foodFlag = 0;
                          }
                      }
                  }
              }
          }
      }

      共享数据类

    • public class Desk {
          /*作用:控制生产者和消费者的执行*/
      
          //是否有食物 0:没有食物  1:有食物
          public static int foodFlag = 0;
      
          //总个数
          public static int count = 10;
      
          //锁对象
          public static Object lock = new Object();
      }

      测试类

    • public class TreadDemo {
          public static void main(String[] args) {
              /*完成生产者和消费者(等待唤醒机制)的代码
              实现线程协作*/
              
              //创建线程的对象
              Cook c = new Cook();
              Foodie f = new Foodie();
      
              //赋值
              c.setName("肯德基");
              f.setName("疯狂的河马吃客");
              //启动
              c.start();
              f.start();
          }
      }

      运行结果

    • 等待唤醒机制(阻塞队列方式实现)
      • 阻塞队列的继承结构
  • 代码实现
  • 生产者类
  • public class Cook extends Thread {
        //阻塞队列
        ArrayBlockingQueue<String> queue;
    
        //构造方法
        public Cook(ArrayBlockingQueue<String> queue) {
            this.queue = queue;
        }
    
        @Override
        public void run() {
            while (true) {
                //不断的把食物放到阻塞队列中
                try {
                    queue.put("一个汉堡");
                    System.out.println("肯德基制作了一个汉堡");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    消费者类

  • public class Foodie extends Thread {
        //阻塞队列
        ArrayBlockingQueue<String> queue;
    
        //构造方法
        public Foodie(ArrayBlockingQueue<String> queue) {this.queue = queue;}
    
        @Override
        public void run() {
            while (true) {
                //不断从阻塞队列获取食物
                try {
                    String food = queue.take();
                    System.out.println("吃货正在吃" + food);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    测试类

  • public class ThreadDemo {
        public static void main(String[] args) {
            /*
             * 需求:利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码
             * 细节
             *           生产者和消费者必须使用同一个阻塞队列*/
    
            //1.创建阻塞队列的对象
            ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
    
            //创建线程对象,把阻塞队列传递过去
            Cook cook = new Cook(queue);
            Foodie foodie = new Foodie(queue);
    
            //开启
            cook.start();
            foodie.start();
        }
    }

    运行结果程序一直在走,这是属于正常现象

  • 因为使用阻塞队列时,线程会一直运行是因为线程会阻塞等待队列中有数据可供处理,从而避免线程空转浪费 CPU 资源。


  • 09 线程六个状态
    •  
      • JVM没有运行状态
      • 当线程抢夺到CPU的执行权,JVM会把当前线程交给操作系统管理

  • 10 线程池
    • 之前线程的弊端
    • 线程池
      • 核心原理
      • 代码实现
        •  
          • 实际场景:不用关闭线程池
        •  
          • public static ExecutorService newCachedThreadPool()的上限是int最大值
          • 方法补充
            public static ExecutorService newSingleThreadExecutor()                                                 创建一个单线程的线程池
                        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个固定大小的线程池,支持定时及周期性任务执行
                        public static ExecutorService newWorkStealingPool()                                                     创建一个使用所有可用 CPU 核心数的线程池
                        public static ExecutorService newWorkStealingPool(int parallelism)                               创建一个使用指定并行线程数的线程池*/
            

          • 代码实现
          • public class MyRunnable implements Runnable{
                @Override
                public void run(){
                    for (int i = 0; i <=100; i++){
                        System.out.println(Thread.currentThread().getName()+"#"+i);
                    }
                }
            }

            测试类

          • public class MyThreadPoolDemo {
                public static void main(String[] args) throws InterruptedException {
                    /*  public static ExecutorService newCachedThreadPool()                                                  创建一个没有上限的线程池
                        public static ExecutorService newFixedThreadPool(int nThreads)                                  创建一个固定大小的线程池
                        public static ExecutorService newSingleThreadExecutor()                                              创建一个单线程的线程池
                        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个固定大小的线程池,支持定时及周期性任务执行
                        public static ExecutorService newWorkStealingPool()                                                     创建一个使用所有可用 CPU 核心数的线程池
                        public static ExecutorService newWorkStealingPool(int parallelism)                               创建一个使用指定并行线程数的线程池*/
            
            
                    //获取线程池对象
                    ExecutorService pool1 = Executors.newFixedThreadPool(4);
                    //提交任务
                    pool1.submit(new MyRunnable());
                    Thread.sleep(10);
                    pool1.submit(new MyRunnable());
                    Thread.sleep(10);
                    pool1.submit(new MyRunnable());
                    pool1.submit(new MyRunnable());
                    pool1.submit(new MyRunnable());
                    pool1.submit(new MyRunnable());
            
                    //关闭线程池
            //        pool1.shutdown();
                }
            }

      • 自定义线程池
      • 任务拒绝策略
      • 线程池大小
        • 最大并行数
          • 为什么+1
            • 将并行数量设置为CPU核心数量加一可以确保系统的所有核心在任何时候都有任务在执行,避免出现空闲核心的情况,并提高系统的整体效率。
        • 线程池多大合适呢
          • 对于线程池的运行情况可以通过获取线程堆栈信息(thread dump)来进行分析和调优,以解决线程池卡死、线程过多等问题
        • 两种运算方式的使用场景
          • CPU密集型运算:
            • 使用场景:适用于需要大量的算术或逻辑运算的任务,如复杂的数值计算、加密算法、图形渲染等。
            • 例子:科学计算、密码解密、图像处理等。
            • 优化策略:利用多线程、并行计算、CPU优化指令集等方式提高计算效率。
          • IO密集型运算:
            • 使用场景:适用于需要频繁进行输入输出操作的任务,如文件读写、网络请求、数据库查询等。
            • 例子:Web服务器、数据库服务器、文件传输等。
            • 优化策略:使用异步IO操作、缓存、批量处理等方式减少IO等待时间,提高系统的IO吞吐量

  • 61
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值