• Java多线程【三种实现方法】


    java多线程

    image

    并发与并行

    并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行
    并行:一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。
    对比:并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)

    进程与线程

    进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度基本单位,是操作系统结构的基础

    线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

    image

    java的线程

    java多线程机制

    多线程是指一个应用程序同时存在几个执行体,按照几条不同的执行线索共同工作的情况。java多线程机制,它使得编程人员可以很方便的开发出具有多线程功能、能同时处理多个任务的强大应用程序。java内置对多线程的支持,java虚拟机可以快速的从一个线程切换到另一个线程。这些线程的轮流执行使得每个线程都有机会使用cpu资源。

    java的主线程

    每个java程序都含有一个线程,那就是主线程(main线程)。Java应用程序都是从主类main方法执行的,当jvm加载代码,发现卖弄方法之后,就会启动一个线程,这个线程就是主线程,负责执行main方法。如果在主线程里面创建其他线程,就会在主线程和其他线程来回切换,直到其他所有线程结束才会结束主线程。
    image

    线程的生命周期

    image

    • 运行
      线程创建之后只占有了内存资源,在JVM管理的线程中并没有刚刚创建的这个线程,只有这个线程调用start()方法后,JVM才知道有一个新线程进入队列等待cpu调用。

    • 中断原因(4种)

      • jVM把cpu资源切换给其他线程。
      • 线程使用cpu期间,执行了sleep(int millsecond)方法,使当前线程进入休眠状态,调用该方法之后会立即让出cpu,经过参数millsecond指定的毫秒后,重新加入队列等待cpu。
        -使用cpu期间,执行了wait()方法,使得当前进程进入等待状态,这个等待个sleep()不同,这个等待需要其他线程调用notify()方法唤醒该线程,此线程才会重新进入队列,等待cpu。
        线程使用cpu期间,执行了某个操作进入阻塞状态,例如(读、写、打印等),只有这些造成阻塞的原因完成,这个线程才会进入队列,等待cpu。

    创建线程(3种)

    继承Thread类实现线程创建

    • 一种是创建一个类继承Thread类,这种继承可以重复使用!
    • 一种是直接使用匿名内部类继承,这种类型只能使用一次,每次使用都要重新创建
    • 不论哪种都需要重写run()方法,并且在定义之后调用start()方法,把这个线程调入线程队列等待调用。

    下面我们使用匿名内部类创建一个打印100以内的奇数线程,使用类继承Thread类打印100内的偶数线程。

    复制代码
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    er-hljs
    package hello; public class Hello { public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread thread = new Thread() { @Override public void run() { for (int i = 0; i < 100; i++) { if (i % 2 == 1) { //打印线程名,线程名是从0开始的 System.out.println(Thread.currentThread().getName() + ":" + i); } } } }; thread1.start(); thread.start(); } } class Thread1 extends Thread{ @Override public void run() { super.run(); for(int i=0;i<100;i++){ if (i%2==0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } }

    方法说明
    start()启动当前线程;调用当前线程的run()方法
    run():需要重写Thread类中的此方法,将创建线程需要执行的操作声明在此方法中
    currentThread():返回执行当前代码的线程
    getName():获取当前线程的名字
    setName(String name):设置当前线程的名字
    yield():释放当前CPU的执行权
    join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完之后,线程a在结束阻塞状态
    sleep(int millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前进程是阻塞状态
    isAlive():判断当前线程是否存活(线程执行完之前都是存活的)

    实现Runnable接口

    • 我们还是创建两个线程,一个打印奇数,一个打印偶数,但是有一个线程每次调用会睡眠(阻塞)10ms。
    • 使用实现接口Runnable方法,必须重写run()方法。
    复制代码
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    er-hljs
    package hello; public class Hello { public static void main(String[] args) { Thread thread = new Thread(new MyThread1()); /* Thread thread = new Thread(new MyThread1()); 就相当于 ,就是创建实现类的对象,再把这个对象用Thread()构造器的方法创建 MyThread1 myThread1 = new MyThread1(); Thread thread = new Thread(myThread1); */ Thread thread1 = new Thread(new MyThread2()); thread.start(); thread1.start(); } } class MyThread1 implements Runnable{ @Override public void run() { for(int i=0;i<100;i++) { if (i % 2 == 1) { System.out.println(Thread.currentThread().getName() + ":" + i); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } } class MyThread2 implements Runnable{ @Override public void run() { for(int i=0;i<100;i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } }

    开发中,优先选择实现Runnable接口的方式创建线程

    原因:

    实现Runnable接口的方式没有类的单继承性的局限性(一个类只能继承一个父类,继承了Thread类就不能在继承其他类了)
    实现Runnable接口的方式更适合来处理多个线程之间有共享数据的情况

    实现Callable接口

    Runnable接口是没有返回值的 Callable有返回值,可以抛出异常
    Thread类并不接受Callable对象。可以使用FutureTask类实现Runnable接口和Future接口
    Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

    Java的类是单继承的设计,如果采用继承Thread的方式实现多线程,则不能继承其他的类,采用接口能够更好的实现数据共享

    FutureTask有两个构造函数,一个以Callable为参数,另外一个以Runnable为参数。

    image

    我理解的就是通过FutureTask把Callable变成通过Runnable接口创建的,因为FutureTask继承了Runnable接口。主要原因是Thread类不接受Callable创建,但是接受Runnable创建的线程。

    复制代码
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    er-hljs
    package hello; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Hello { public static void main(String[] args) throws Exception { MyThread1 myThread1 = new MyThread1(); FutureTask<Integer> futureTask = new FutureTask<>(new MyThread1()); new Thread(futureTask).start();//开启线程 System.out.println(futureTask.get());//获取返回值 } } class MyThread1 implements Callable<Integer> { @Override public Integer call() throws Exception { int count = 0; for (int i = 1;i<100;i++){ if (i%3==0){ count++; } } return count; } }

    线程池

    image

    线程池的执行过程
    image

    • 实例
    复制代码
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    er-hljs
    package hello; import java.util.concurrent.*; public class Hello { public static void main(String[] args) throws Exception { //创建线程池 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 5, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(2), new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName("myThread"); return thread; } }, new ThreadPoolExecutor.AbortPolicy()); //threadPoolExecutor.submit(); threadPoolExecutor.execute(new MyThread());//提交任务 threadPoolExecutor.shutdown();//关闭线程池 } } class MyThread implements Runnable{ @Override public void run() { for (int i=0;i<10;i++){ System.out.println(i); } } }
  • 相关阅读:
    【死磕JVM】看完这篇我也会排查JVM内存过高了 就是玩儿!
    android RecyclerView列表自动播放实现
    毕设-基于SpringBoot学生住宿管理系统
    神经网络在电商中的应用,神搜电商科技
    Eureka添加@Loadbalanced 报错 No instances available for XXXXX
    使用51单片机控制lcd1602字体显示
    sap 学习笔记(2022/11/16)
    数据库事务中“锁”的分类
    Java:如何去优雅地优化接口
    scaner 从外网到内网域渗透
  • 原文地址:https://www.cnblogs.com/hjk-airl/p/16041437.html