多线程重要知识点以及线程和线程池的创建

1.多线程

1.1易混概念

进程:拥有独立内存空间的应用程序,每个进程的内存空间是独立的
线程:是进程中的执行路径,共享内存空间,线程之间可以自由切换,进 程至少包含一个线程
线程实际上是进程基础之上的进一步划分,一个进程启动后,里面若干执行路径又可以划分若干线程


线程阻塞(所有耗时操作):所有比较消耗时间的操作。例:读取文件,接收用户输入。


多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

每个线程都有自己的栈空间,但是共用堆内存


同步:排队执行,效率低但是安全。
异步:同时执行,效率高但是数据不安全


并发:在指定的时间段内同时发生的事件
并行:同一刻发生,同时发生。

1.2线程的六种状态

1.new:线程刚被创建,还未启动时的状态。
2.Runnable:正在执行
3.Blocked:阻塞
4.Waiting:休眠
5.TimedWaiting:指定时间的休眠
6.Terminated:线程结束(死亡)

1.3线程的创建与执行方法

1.3.1创建及执行

创建方式:总共三种.
(1)创建自己的类继承Thread类,重写run方法(写进去自己要执行的内容)最后执行时,调用Thread类的start()方法。
(2)创建自己的类实现Runnable接口,重写run方法(写进去自己要执行的内容),执行时:创建自己写的类的对象、Thread类对象,将自己的类对象名传入Thread参数列表内,Thread类调用start()方法。
(3)创建自己的类实现Callable接口,实现call方法(写进去自己要执行的内容),执行时:创建自己写的类的对象、FutureTask类的对象、Thread类对象,将自己的类对象名传入FutureTask参数列表内,Thread类调用start()方法。

具体创建操作:
第一种:Thread

public static void main(String[] args) {
        /**
         * 创建对象并执行
         * 调用start()方法执行
         */
        MyThread myThread = new MyThread();
        myThread.start();
    }
    
    public static class MyThread extends Thread{
        /**
         * 重写run方法
         */
        @Override
        public void run() {
            System.out.println("要进行的操作");
        }
    }

第二种:Runnable+Thread

public static void main(String[] args) {
        /**
         * 创建对象并传任务,然后执行
         * 调用start()方法执行
         */
        //任务对象
        MyRunnable myRunnable = new MyRunnable();
        //线程对象,分配任务
        Thread t = new Thread(myRunnable);
        t.start();
    }

    public static class MyRunnable implements Runnable{
        /**
         * 实现run方法
         */
        @Override
        public void run() {
            System.out.println("要进行的操作");
        }
    }

第三种:Callable+Thread

   public static void main(String[] args) {
     Callable<Integer> callable = new MyCallable();
     FutureTask<Integer> futureTask = new FutureTask<>(callable);
     Thread thread = new Thread(futureTask);
     thread.start();
 }

 public static class MyCallable implements Callable<Integer> {
     /**
      * 实现call方法
      * @return
      * @throws Exception
      */
     @Override
     public Integer call() throws Exception {
         System.out.println("要进行操作的地方");
         //返回值根据泛型可以调整
         return 0;
     }
 }

1.3.2三种方法的对比

实现Runnable与继承Thread相比
优势:
1-创建任务的方式然后给线程分配,来实现多线程,更适合多个线程同时执行相同任务
2-可以避免单继承所带来的局限性
3-任务与线程本身是分离的,提高了健壮性
4-线程池技术,接受Runnable类型的任务,而不接受Thread类型的任务。


Callable和Runnable相同点:
1-都是接口
2-都可以编写多线程程序
3-都采用Thread.start()启动
Callable和Runnable不同点:
1-Runnable没有返回值,Callable有返回值。
2-Runnale接口的run()不能抛出异常,Callable的call()能抛出异常。
Callable获取返回值:
Callable接口支持返回执行结果,通过FuturTask.get()获取,此方法会阻塞主线程执行,不调用就不会阻塞。


1.4线程安全问题

1.同步代码块
格式:synchronized(锁对象){}
锁对象通常定义一个Object对象。用大括号,括起来要锁的位置上

2.同步方法
以方法为单位进行加锁
直接给方法添加synchronized
它的锁是这个方法的this,如果是静态方法,锁的是:类名.class

3.显式锁
Lock类 ,子类:ReentrantLock()
使用方法:

Lock l = new ReentrantLock();
l.lock();//锁
.
.要锁住的内容
.
.
l.unlock();//开锁

2.线程池Executors

为什么要有线程池:
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

线程池好处:
1-降低资源消耗
2-提高响应速度(不用每次都创建线程)直接拿线程池里的线程
3-提高线程的可管理性

2.1缓存线程池

无限制长度
任务加入后的执行流程:
1.判断线程池是否存在空闲线程 2存在则使用 3不存在则创建线程并使用

        ExecutorService e = Executors.newCachedThreadPool();
        //指挥线程执行任务
        e.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"操作");
            }
        });
        e.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"操作");
            }
        });
        e.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"操作");
            }
        });
        //缓存1000ms
        try {
            Thread.sleep(1000);
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }
        //第四次执行的线程是缓存的之前的线程
        e.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"操作");
            }
        });      
        

2.2定长线程池

长度是指定的线程池

加入任务后的执行流程:
1 判断线程池是否存在空闲线程
2 存在则使用
3 不存在空闲线程 且线程池未满的情况下 则创建线程 并放入线程池中 然后使用
4 不存在空闲线程 且线程池已满的情况下 则等待线程池的空闲线程

      //长度固定为了2(只能两个线程同时执行)
      ExecutorService service = Executors.newFixedThreadPool(2);
      service.execute(new Runnable() {
          @Override
          public void run() {
              System.out.println(Thread.currentThread().getName()+"操作");
              try {
                  Thread.sleep(3000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }

          }
      });
      service.execute(new Runnable() {
          @Override
          public void run() {
              System.out.println(Thread.currentThread().getName()+"操作");
              try {
                  Thread.sleep(3000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }

          }
      });

2.3单线程线程池

执行多少次都是一个线程
执行流程
1 判断线程池的那个线程是否空闲
2 空闲则使用
3 不空闲则等待它空闲后再使用

ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"哈哈");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"哈哈");
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"哈哈");
            }
        });

2.4周期任务定长线程池

执行流程
1 判断线程池是否存在空闲线程
2 存在则使用
3 不存在空闲线程 且线程池未满的情况下 则创建线程 并放入线程池中 然后使用
4 不存在空闲线程 且线程池已满的情况下 则等待线程池的空闲线程

 /**
 *定时执行
 */
 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
       //定时执行一次
       //参数1:定时执行的任务
       //参数2:时长数字
       //参数3:参数2的时间单位    Timeunit的常量指定
      /* scheduledExecutorService.schedule(new Runnable() {
           @Override
           public void run() {
               System.out.println(Thread.currentThread().getName()+"嘿嘿");
           }
       },5, TimeUnit.SECONDS);      //5秒钟后执行*/    
      /*
      周期性执行任务
          参数1:任务
          参数2:延迟时长数字
          参数3:周期时长数字(每隔多久执行一次)
          参数4:时长数字的单位
       */
      scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
          @Override
          public void run() {
              System.out.println(Thread.currentThread().getName()+"锄禾日当午");
          }
      },5,1,TimeUnit.SECONDS);

猜你喜欢

转载自blog.csdn.net/apple_51491580/article/details/113405600