详解Java实现多线程的三种方式

 更新时间:2022年03月23日 10:47:51   作者:hjk-airl  
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。本文总结了Java多线程是三种实现方式,需要的可以参考一下

并发与并行

并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行

并行:一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。

对比:并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)

进程与线程

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

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

java的线程

java多线程机制

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

java的主线程

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

线程的生命周期

运行

线程创建之后只占有了内存资源,在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内的偶数线程。

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()方法。

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为参数。

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

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;
    }
}

线程池

线程池的执行过程

实例

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);
        }
    }
}

以上就是详解Java实现多线程的三种方式的详细内容,更多关于Java多线程的资料请关注脚本之家其它相关文章!

相关文章

  • Springboot+Shiro+Mybatis+mysql实现权限安全认证的示例代码

    Springboot+Shiro+Mybatis+mysql实现权限安全认证的示例代码

    Shiro是Apache 的一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理,Shiro 主要分为两个部分就是认证和授权两部分,这篇文章主要介绍了Springboot+Shiro+Mybatis+mysql实现权限安全认证的示例代码,需要的朋友可以参考下
    2024-07-07
  • 谈谈Java中Volatile关键字的理解

    谈谈Java中Volatile关键字的理解

    volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果,本文给大家介绍java中volatile关键字,需要的朋友参考下
    2016-03-03
  • springboot 自定义属性与加载@value示例详解

    springboot 自定义属性与加载@value示例详解

    在SpringBoot框架中,自定义属性通常通过application.properties文件配置,并使用@Value注解加载,虽然这是一种可行的方法,但存在一种更优雅的实现方式,本文给大家介绍springboot 自定义属性与加载@value的相关操作,感兴趣的朋友一起看看吧
    2024-10-10
  • Java Thread多线程开发中Object类详细讲解

    Java Thread多线程开发中Object类详细讲解

    这篇文章主要介绍了Java Thread多线程开发中Object类,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-03-03
  • java 判断两个时间段是否重叠的案例

    java 判断两个时间段是否重叠的案例

    这篇文章主要介绍了java 判断两个时间段是否重叠的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • java模拟实现银行ATM机操作

    java模拟实现银行ATM机操作

    这篇文章主要为大家详细介绍了java模拟实现银行ATM机操作,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • java中多个@Scheduled定时器不执行的解决方法

    java中多个@Scheduled定时器不执行的解决方法

    在应用开发中经常需要一些周期性的操作,比如每5分钟执行某一操作等,这篇文章主要给大家介绍了关于java中多个@Scheduled定时器不执行的解决方法,需要的朋友可以参考下
    2023-04-04
  • spring-cloud入门之eureka-server(服务发现)

    spring-cloud入门之eureka-server(服务发现)

    本篇文章主要介绍了spring-cloud入门之eureka-server(服务发现),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-01-01
  • Java发送邮箱验证码、session校验功能

    Java发送邮箱验证码、session校验功能

    本篇主要描述“发送邮箱验证码、session校验”相关前(html\js)后(java)台代码,业务逻辑示例,需要的朋友可以参考下
    2018-02-02
  • springboot整合vue项目(小试牛刀)

    springboot整合vue项目(小试牛刀)

    这篇文章主要介绍了springboot整合vue项目(小试牛刀),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09

最新评论