首页>>后端>>java->Java多线程的4种实现方式详解以及案例演示

Java多线程的4种实现方式详解以及案例演示

时间:2023-12-01 本站 点击:0

Java中多线程的实现方式很多,本文介绍四种方法,分别是继承Thread、实现Runnable、实现Callable、使用线程池。在最后章节给出了全部方式的演示。

1 继承Thread类

1.1 步骤

创建一个类,继承Thread类,该类具备多线程的特征。

重写Tread类的run方法,在run方法当中定义线程任务。Run()方法体又叫线程执行体。

创建Tread类的子类的实例,即创建了线程对象。通过调用该对象的start方法开启一条新线程,调用start方法之后java虚拟机会自动调用该线程的run方法。

classThread1extendsThread{@Overridepublicvoidrun(){//线程任务....System.out.println("继承thread");}}Thread1thread1=newThread1();thread1.start();

1.2 为什么要继承Thread类?为什么不直接创建Thread对象?

new Thread类得到一个线程对象是没有任何问题的,调用start()开启了一个新的线程,此时JVM会自动去调用run方法,但是run方法当中没有任何的内容。

所以需要定义一个类,继承Thread类,此时就可以重写run方法,把线程任务定义在run方法当中即可。

1.3 Thread相关方法

//获得当前线程对象;//当一个类继承Tread类时,run方法中直接使用this关键字即可获取当前线程对象,而this关键字可以省略。staticThreadcurrentThread()//获得线程的名称:run方法中直接getName();//线程的名称:默认情况是Thread-0开始,数字依次往后增加,当然可以自定义线程的名称;每个线程都有一个标识名,并且多个线程可以同名。//主线程默认名称:main主线程的执行体:main方法StringgetName()//自定义线程名称,run方法中直接setName(Stringname);voidsetName(Stringname)//测试线程是否处于活动状态。就绪态和运行态就是活动状态.booleanisAlive()

1.4 名称为什么是:Thread-? 编号

这个简单,我们进入Thread类看看源码就知道了。

publicclassThreadimplementsRunnable{//定义名字的变量privatevolatileStringname;//定义线程计数器privatestaticintthreadInitNumber;privatestaticsynchronizedintnextThreadNum(){//调用该方法,计数器自增1returnthreadInitNumber++;}//构造器Thread(Runnabletarget,AccessControlContextacc){//调用构造器,此处将名字设为"Thread-",调用计数器方法init(null,target,"Thread-"+nextThreadNum(),0,acc,false);}//initprivatevoidinit(ThreadGroupg,Runnabletarget,Stringname,longstackSize,AccessControlContextacc,booleaninheritThreadLocals){if(name==null){thrownewNullPointerException("namecannotbenull");}//给名字初始化this.name=name;//.....省略了代码}}

2 实现Runnable接口

2.1 步骤

创建一个类A:实现Runnable接口,并重写run方法,在run方法内定义线程任务。

创建Runnable接口实现类的实例并且作为一个target传递给Thread类的构造器创建一个Thread类对象,此时Runnable接口和Thread类就具有联系,而该Thread对象才是真正的线程对象。

使用Thread对象的start方法开启一个新的线程

注意:当类实现Runnable接口时,只能用Thread.currentThread()方法获取当前线程对象。

2.2 Runnable接口

Runnable为非Thread子类的类提供了一种激活方式。激活的意思是说某个线程已启动并且尚未停止。通过实例化某个 Thread 实例并将自身作为运行目标,就可以运行实现 Runnable 的类而无需创建 Thread 的子类。

大多数情况下,如果只想重写run()方法,而不重写其他Thread 方法,那么应使用 Runnable 接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。Java应该慎用继承。

唯一的方法: void run(); 要想实现该接口必须重写该方法!

3 实现Callable接口

3.1 概述

Java5使用Callable和Future创建线程,一般和ExecutorService连用。

和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大:

call()方法可以有返回值。

call()方法可以声明抛出异常。

Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。

3.2 相关方法

//试图取消该Future里面关联的Callable任务booleancancel(booleanmayInterruptIfRunning)//返回Callable里call()方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值。Vget()//返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutExceptionVget(longtimeout,TimeUnitunit)//若Callable任务完成,返回TruebooleanisDone()//如果在Callable任务正常完成前被取消,返回TruebooleanisCancelled()

3.3 步骤

创建一个类A实现Callable接口,重写call方法定义线程任务。

新建FutureTask类,传入类A,该类可以获取返回值。

新建Thread类传入FutureTask对象,使用start方法开启线程。

使用FutureTask类的get方法获取返回值。

3.4 三种实现方式的比较

3.4.1 继承Thread类

实现简单,直接继承Thread即可。并且在run()方法内获取当前线程直接使用this 就可以了,无须使用Thread.currentThread() 方法;

java当中的继承只能是单继承,一个类只能继承一个直接父类。如果一个类继承了Thread类,就不能有其他的父类。

创建的对象,即封装了线程任务,又封装了线程对象的特点(线程对象),不同的东西封装到了一个对象之中,不符合java面向对象的特点。

3.4.2 实现Runnable接口

run()方法内获取当前线程不能使用使用this,必须使用Thread.currentThread() 方法;

将线程任务单独的封装到一个接口实现类当中,将线程任务和线程对象进行了分离:Runnable接口实现类当中定义线程任务,Thread类封装了线程对象,将不同的功能封装到不同的对象之中,这种思想更加的符合面向对象的特点。

避免了单继承带来的局限性。

适合多条线程处理同一个资源的情况,很容易的实现资源共享。需要多个线程完成一个任务时,比如:多个线程共同卖100张票,他们需要共享100张票这个资源。

3.4.3 实现Callable接口

具有实现Runnable接口的特点,同时还能获取返回值,并且可以抛出异常。

4 使用线程池工具

线程池提供了一个线程队列,队列中保存着所有等待状态的线程,避免了创建与销毁额外开销,提高了响应的速度,建议大家使用这种方法创建、管理线程。

体系结构:

java.util.concurrent.Executor : 负责线程的使用与调度的根接口 |--ExecutorService 子接口: 线程池的主要接口 |--ThreadPoolExecutor 线程池的实现类 |--ScheduledExecutorService 子接口:负责线程的调度 |--ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor,实现 ScheduledExecutorService

工具类 : Executors

ExecutorService newFixedThreadPool() : 创建固定大小的线程池 ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。 ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程 ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。

5 多线程创建方式演示

publicclassThreadCreate{publicstaticvoidmain(String[]args)throwsExecutionException,InterruptedException{//main线程System.out.println("main:"+Thread.currentThread().getName());//方式一Thread1thread1=newThread1();thread1.start();//方式二Threadthread=newThread(newThread2());thread.start();//方式三FutureTask<String>stringFutureTask=newFutureTask<String>(newThread3());Threadthread2=newThread(stringFutureTask);thread2.start();//获取返回值Strings=stringFutureTask.get();System.out.println(s);//方法四:线程池ExecutorServiceexecutorService=Executors.newFixedThreadPool(10);executorService.execute(()->System.out.println("线程池方式:"+Thread.currentThread().getName()));executorService.shutdown();}}classThread1extendsThread{@Overridepublicvoidrun(){System.out.println("继承THread:"+this.getName());}}classThread2implementsRunnable{@Overridepublicvoidrun(){System.out.println("实现Runnable:"+Thread.currentThread().getName());}}classThread3implementsCallable{@OverridepublicObjectcall(){System.out.print("实现Callable:");returnThread.currentThread().getName();}}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/5446.html