• 多线程(线程的四种创建方式)


    1、并发与并行

    并行:两个或多个事件在同一时刻发生

    并发:两个或多个事件在同一时间段内发生

    2、进程与线程

    打开浏览器中的百度、淘宝,那么浏览器是一个进程,淘宝和百度是两个线程。

    3、线程创建的方式

    (1)继承Thread类:

    自定义线程:

    public class MyThread extends Thread {
        public void run(){
            for(int i=0;i<10;i++){
                System.out.println("thread:"+new Date().getTime());
            }
        }
    }

    创建测试类:

    
    
    public static void main(String args []){
    MyThread myThread=new MyThread();
    myThread.start();
    for(int i=0;i<10;i++){
    System.out.println("main:"+new Date().getTime());
    }
    }

    main方法也是一个线程被称作主线程,手动创建的线程称为子线程,两个线程中分别书写for循环。

    main:1585656576673
    main:1585656576674
    main:1585656576674
    main:1585656576675
    main:1585656576675
    main:1585656576675
    main:1585656576675
    main:1585656576675
    main:1585656576675
    main:1585656576675
    thread:1585656576675
    thread:1585656576676
    thread:1585656576676
    thread:1585656576677
    thread:1585656576678
    thread:1585656576678
    thread:1585656576678
    thread:1585656576678
    thread:1585656576678
    thread:1585656576679

    继承Thread类,重写run方法,创建线程的对象并调用start开启线程(注意:调用的是start方法,不是run方法)。这里循环的次数较少是先执行main线程,再去执行另外一个线程,当循环的次数较多时会出现两个线程的交叉执行的现象。

     这里只开辟了一条线程,可以开辟多条线程,主线程和子线程是交替执行的

    (2)实现Runnable接口:

    实现接口,创建线程:

    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println("myRunnable:"+new Date().getTime());
            }
        }
    }

    测试类:

    public class Test {
        public static void main(String args []){
            Thread thread=new Thread(new MyRunnable());
            thread.start();
            for(int i=0;i<10;i++){
                System.out.println("main:"+new Date().getTime());
            }
        }
    }
    main:1585657253215
    main:1585657253215
    main:1585657253215
    main:1585657253216
    main:1585657253216
    main:1585657253216
    main:1585657253216
    main:1585657253216
    main:1585657253216
    main:1585657253216
    myRunnable:1585657253217
    myRunnable:1585657253217
    myRunnable:1585657253217
    myRunnable:1585657253218
    myRunnable:1585657253218
    myRunnable:1585657253218
    myRunnable:1585657253218
    myRunnable:1585657253219
    myRunnable:1585657253219
    myRunnable:1585657253219

    需要创建线程对象,通过线程对象来开启我们的线程,采用的是代理的方式

    (3)实现Callable接口

    Callable接口:

    Callable需要使用FutureTask类帮助执行,FutureTask类结构如下:

     RunnableFuture接口需要实现Future和Runnable两个接口。

    Future接口的方法:

    判断任务是否完成:isDone()

    能够中断任务:cancel()

    能够获取任务的执行结果:get()

    创建线程:

    public class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"执行时间"+new Date().getTime());
            }
            return "MyCallable接口执行完成!!";
        }
    }

    在创建线程的时候设置了返回值,可以通过get方法获取。

    主线程:

    public class Test {
        public static void main(String args[]){
            FutureTask<String> futureTask =new FutureTask<String>(new MyCallable());
            Thread thread=new Thread(futureTask,"Mycallable");
            thread.start();
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"执行时间"+new Date().getTime());
            }
            String result= null;
            try {
                result = futureTask.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            System.out.println(result);
        }
    }

    在主线程中可以获取到手动创建的线程的返回值。

    4、创建线程池

    分类:

    newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需求可灵活回收空闲线程,若无可回收,则新建线程
    newFixedThreadPool:创建一个定长线程池,可控制线程的最大并发数,超出的线程会在线程池中等待。
    newScheduleThreadPool: 创建一个定长线程池,支持定时及周期性任务处理
    newSingleThreadScheduledExecutor:创建一个单线程化的线程池,他只用唯一的工作栈来执行任务

    使用:

    复制代码
    class Mythread implements Runnable {
       public void run(){
           System.out.println(Thread.currentThread().getName());
       }
       public static  void  main(String args []){
               ExecutorService executorService = Executors.newFixedThreadPool(3);// 
               // Executors:线程池创建工厂类,调用方法返回线程池对象
               executorService.submit(new Mythread());
               executorService.submit(new Mythread());
               executorService.submit(new Mythread());
           }
    }
    复制代码
    pool-1-thread-1
    pool-1-thread-2
    pool-1-thread-3

    好处:

    限制线程的个数,不会导致由于线程过多导致系统运行缓慢,甚至崩溃

    节省了资源:我们用创建的线程,在使用后都会被销毁,频繁地创建和销毁会造成时间和资源的浪费。线程池是一个能够容纳多个线程的容器,里面的线程可以反复使用。

    5、实现接口和继承Thread类比较

    (1)接口更适合多个相同的程序代码的线程去共享同一个资源。

    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<10;i++){
                System.out.println("myRunnable:"+new Date().getTime());
            }
        }
    }

    可以new多个Thread去调用创建的线程中的run方法,代码的复用增强:

     Thread thread=new Thread(new MyRunnable());

    (2)接口可以避免Java中的单继承的局限性
    (3)接口代码可以被多个线程共享,代码和线程独立
    (4)线程池只能放入实现Runable 或callable 接口的线程,不能直接放入继承Thread的类

    扩充:

    在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。


    6、Runnable和Callable接口比较
    相同点:
    (1)两者都是接口

    (2)两者都可用来编写多线程程序
    (3)两者都需要调用Thread.start 启动续程

    不同点:
    (1)实现Callable接口的线程能返回执行结果,而实现Runnable 接口的线程不能返回结果
    (2)Callable 接口的call()方法允许抛出异常,而 Runnable 接口的run()方法的不允许批异常

    (3)实现Callable 接口的线程可以调用Future.cancel取消执行,而实现runnable 接口的线程不能,不需要控制线程的话没必要用Callable接口

    注意点:

    Callable 接口支持返回执行结果,此时需要调用FutureTask. get()方法(主线程会被阻塞,不能和main并发执行)实现,此方法会阻塞主线程直到获取“将来’结果;当不调用此方法时,主线程不会阻塞!

    7、线程的生命周期

    (1)新建

    new关键字创建一个线程之后,该线程处于新建状态。jvm为线程分配内存,初始化成员变量值

    (2)就绪

    当线程对象调用了start()方法后,该线程处于就绪状态。jvm为线程创建方法栈和程序计数器,等待线程调度器调度

    (3)运行

    就绪状态的线程获得CPU资源,开始运行run方法,该线程处于运行状态

    (4)阻塞

    线程调用sleep方法主动放弃所占用的处理器资源

    线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞

    线程试图获得一个同步锁(同步监视器),但是该锁正在被其它线程持有

    线程正在等待某个通知

    程序调用了线程的suspend()方法将该线程挂起,但是这个方法容易导致死锁,所以应该尽量避免使用该方法

    (5)死亡

    线程会以如下的三种方式结束,结束后就进入死亡状态:·

    run()或call()方法执行完成,线程正常结束

    线程抛出一个未捕获的Exception或Error

    调用该线程的stop方法来结束该线程,该方法容易导致死锁,不推荐使用

    进程的三种基本状态:

    就绪:进程已经获得了除CPU以外的所有必要资源,只要获得了CPU便可立即执行。

    执行:进程已经获得了CPU,其进程正在执行的状态。

    阻塞:正在执行的进程由于IO请求、申请缓冲区失败等(如:访问临界资源)暂时无法继续执行的状态。

     8、线程安全

    如果多个线程同时运行同一个实现了Runnable接口的类,程序每次运行的结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的,反之,则是线程不安全的。

  • 相关阅读:
    Ubuntu 截图工具Flameshot
    django 执行原生的sql
    tensorflow学习笔记(3)前置数学知识
    tensorflow学习笔记(2)-反向传播
    tensorflow学习笔记(1)-基本语法和前向传播
    深度学习-tensorflow学习笔记(1)-MNIST手写字体识别预备知识
    数据结构-查找-散列表的线性探测已经拉链法的查找
    数据结构-查找-折半查找-二叉排序树查找
    数据结构-查找-线性表查找技术
    数据结构-排序-直接插入排序
  • 原文地址:https://www.cnblogs.com/zhai1997/p/12609211.html
Copyright © 2020-2023  润新知