: :其他软件 2020-07-08 00:02:10
线程创建线程的几种方式1、继承Thad类2、实现Runnable接口3、实现Callable接口4、线程池创建线程几种方式线程池详细解析
创建线程的几种方式
在创建线程之前,我们要先熟悉一个类Thad;
ubcclassThadimlementsRunnable{
*MakesugisterNativesisthefirstthing<cnit>does.*
rivatestaticnativevoidgisterNatives();
static{
gisterNatives();
}
*省略一堆*
**
*Ifthisthadwasconstructedusingasearate
*<code>Runnable<>runobject,thenthat
*<code>Runnable<>object's<code>run<>methodiscalled;
*otherwise,thismethoddoesnothingandturns.
*<>
*Subclassesof<code>Thad<>shodoverridethismethod.
*
*@see#start()
*@see#sto()
*@see#Thad(ThadGrou,Runnable,String)
*
@Override
ubcvoidrun(){
if(target!=nl){
target.run();
}
}
}重写的Runnable接口的run方法,为业务运行代码编写的地方,如果我们要实现业务书写,也需要重写run方法,再通过点start方法启动线程,等待CPU调用!
1、继承Thad类
**
*Auther:mayuhang<>
*Date:17:32:2020629<>
*Descrition:方式1.继承方式创建,缺点是因为java是单继承,所以,无法再继承其他类;
*
classExtendsThadextendsThad{
@Override
ubcvoidrun(){
System.out.rintln("继承Thad方式创建线程,重写run方法");
}
启动线程方法见下方
}2、实现Runnable接口
**
*Auther:mayuhang<>
*Date:17:33:2020629<>
*Descrition:方式2.实现runnable接口;接口可以多继承,可以实现更多的接口,完成更为复杂的业务
*
classRunnableImlimlementsRunnable{@Override
ubcvoidrun(){
System.out.rintln("实现Runnable接口方式创建线程,重写run方法");
}
启动线程方法见下方
}3、实现Callable接口
**
*Auther:mayuhang<>
*Date:17:34:2020629<>
*Descrition:方式3,实现callable接口,有返回值,返回完成后的结果值!可以自定义返回值!也可以返回执行业务后的参数值!
*classCallableImimlementsCallable<Object>{
@Override
ubcObjectcall()throwsExcetion{
System.out.rintln("实现Callable接口方式创建线程,重写call方法,有返回值");
turn"输出Callable的返回值";
}
可以通过FutuTask类的其中一个构造方法使用Callable,另外一个构造方法可以使用Runnable
ubcstaticvoidmain(String[]args)throwsExcetion{
CallableImcallableIm=newCallableIm();
FutuTask<Object>objectFutuTask=newFutuTask<>(callableIm);
Thadthad1=newThad(objectFutuTask);
thad1.start();
打印返回值
System.out.rintln(objectFutuTask.get());
}
}4、线程池
第四种,线程池
【强制--来自阿里巴巴Java开发手册】线程池不允许使用Executors去创建,而是通过ThadPoolExecutor的方式,
这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险;
ExecutorServiceexecutorService=Executors.newSingleThadExecutor();
ThadPoolExecutorthadPoolExecutor=newThadPoolExecutor(5,10,5000,TimeUnit.MICROSECONDS,
newArrayBlockingQueue<>(5),newThadPoolExecutor.AbortPocy());
for(inti=0;i<10;i++){
有返回值方式执行业务业务方法,参考第三种线程创建方式Callable;来自接口ExecutorService的submit(Runnabletask)执行
Futu<Object>submit=thadPoolExecutor.submit(newCallable<Object>(){
@Override
ubcObjectcall()throwsExcetion{
System.out.rintln("submit运行线程"+Thad.curntThad().getName());
turn"线程池的submit(Callable)";
}
});
System.out.rintln("------------------"+submit.get());
线程池无返回值方式执行业务方法,调用父类线程池Executor中的execute(Runnablecommand)实现;(newRunnable为匿名内部类!)
thadPoolExecutor.execute(newRunnable(){
@Override
ubcvoidrun(){
System.out.rintln("execute运行线程"+Thad.curntThad().getName());
}
});
}这里重点讲解线程池之前,简单过一下main中,使用线程的几种方式!
创建线程几种方式ubcstaticvoidmain(String[]args){
第一种创建方式:使用继承Thad的对象
ExtendsThadextendsThad=newExtendsThad();
extendsThad.start();
第二种创建方式,使用runnable的实现类
RunnableImlrunnable=newRunnableIml();
创建线程(使用有参构造函数)
Thadthad=newThad(runnable);
启动线程,但是并不一定启动后立马执行哦
thad.start();
第三种,使用无参构造方法,重写Thad的run方法!:也可以直接使用用匿名内部类方式,具体其他创建方式还可查看Thad的其他构造函数(也可以使用匿名内部类)
newThad(){
@Override
ubcvoidrun(){
我们的业务方法
}
}.start();
匿名内部类(来自newRunnable接口!)
newThad(newRunnable(){
@Override
ubcvoidrun(){
我们的run里面写的业务代码!
}
}).start();
lamda表达式,等价于上面那种写法!!
newThad(()->{
我们的run里面写的业务代码!具体lamda表达式,后面找个专题细讲一下!
}).start();线程池详细解析
首先我们来看看线程池的构造函数:
Pubcconstructorsandmethods**
*Catesanew{@codeThadPoolExecutor}withthegiveninitial
*arametersanddefatthadfactoryandjectedexecutionhandler.
*Itmaybemoconvenienttouseoneofthe{@nkExecutors}factory
*methodsinsteadofthisgeneraluroseconstructor.
*
*@aramcoPoolSizethenumberofthadstokeeintheool,even
*iftheyaidle,unless{@codeallowCoThadTimeOut}isset
*@arammaximumPoolSizethemaximumnumberofthadstoallowinthe
*ool
*@aramkeeAveTimewhenthenumberofthadsisgaterthan
*theco,thisisthemaximumtimethatexcessidlethads
*willwaitfornewtasksbefoterminating.
*@aramunitthetimeunitforthe{@codekeeAveTime}argument
*@aramworkQueuethequeuetouseforholdingtasksbefotheya
*executed.Thisqueuewillholdonlythe{@codeRunnable}
*taskssubmittedbythe{@codeexecute}method.
*@throwsIllegalArgumentExcetionifoneofthefollowingholds:*{@codecoPoolSize<0}*{@codekeeAveTime<0}*{@codemaximumPoolSize<=0}*{@codemaximumPoolSize<coPoolSize}
*@throwsNlPointerExcetionif{@codeworkQueue}isnl
*
ubcThadPoolExecutor(intcoPoolSize,
intmaximumPoolSize,
longkeeAveTime,
TimeUnitunit,
BlockingQueue<Runnable>workQueue){
this(coPoolSize,maximumPoolSize,keeAveTime,unit,workQueue,
Executors.defatThadFactory(),defatHandler);
}我们通过这个图,来理解里面的几个参数:第一个参数:intcoPoolSize核心线程,上图绿色方框中的内容!
第二个参数:intmaximumPoolSize最大线程,核心线程和非核心线程的总数!
第三个参数:longkeeAveTime存活时间,非核心线程的存活时间!
第四个参数:TimeUnitunit存活时间单位,来自JUC中的TimeUnit枚举类!
第五个参数:BlockingQueueworkQueue阻塞队列可以new它的实现类,作为这个参数,配置上对应的长度即可比如:newArrayBlockingQueue(5)长度五的阻塞队列!
第六个参数:RejectedExecutionHandlerhandler异常处理方式,为红色部分所示!具体使用方式如下图所示,使用例子:newThadPoolExecutor.DiscardOldestPocy()“丢弃老任务(队头)策略”ackagecom.woniuxy.concurnt.executor;imortjava.util.concurnt.*;**
*Auther:mayuhang<>
*Date:2020615:9:36<>
*Descrition:线程池创建
*
ubcclassExecutorPoolTest1{
volatilestaticintj=0;ubcstaticvoidmain(String[]args){
**
*Auther:mayuhang<>
*Date:15:19:202076<>
*Descrition:表示核心线程5个,总线程数量10个,非核心线程5个存活时间为5000微秒,
*使用newArrayBlockingQueue<Runnable>(5)队列,队列容量5,可以放入5个待执行任务,
*异常处理方式为newThadPoolExecutor.AbortPocy()抛出异常!
*
ThadPoolExecutorthadPoolExecutor=newThadPoolExecutor(5,10,5000,TimeUnit.MICROSECONDS,
newArrayBlockingQueue<>(5),newThadPoolExecutor.AbortPocy());for(inti=1;i<=16;i++){
thadPoolExecutor.submit(newRunnable(){
@Override
ubcvoidrun(){
System.out.rintln("我是"+Thad.curntThad().getName()+"线程,正在执行"+(++ExecutorPoolTest1.j)+
"的任务!");
try{
Thad.slee(300);
}catch(InterrutedExcetione){
e.rintStackTrace();
}
}
},i);
}
}
}
打印结果:
我是ool-1-thad-1线程,正在执行1的任务!
我是ool-1-thad-4线程,正在执行4的任务!
我是ool-1-thad-2线程,正在执行2的任务!
我是ool-1-thad-3线程,正在执行3的任务!
我是ool-1-thad-5线程,正在执行5的任务!
我是ool-1-thad-6线程,正在执行6的任务!
我是ool-1-thad-7线程,正在执行7的任务!
我是ool-1-thad-8线程,正在执行8的任务!
我是ool-1-thad-9线程,正在执行9的任务!
我是ool-1-thad-10线程,正在执行10的任务!
Excetioninthad"main"java.util.concurnt.RejectedExecutionExcetion:Taskjava.util.concurnt.FutuTask@5cad8086jectedfromjava.util.concurnt.ThadPoolExecutor@6e0be858[Running,oolsize=10,activethads=10,queuedtasks=5,comletedtasks=0]
atjava.util.concurnt.ThadPoolExecutor$AbortPocy.jectedExecution(ThadPoolExecutor.java:2047)
atjava.util.concurnt.ThadPoolExecutor.ject(ThadPoolExecutor.java:823)
atjava.util.concurnt.ThadPoolExecutor.execute(ThadPoolExecutor.java:1369)
atjava.util.concurnt.AbstractExecutorService.submit(AbstractExecutorService.java:123)
atcom.woniuxy.concurnt.executor.ExecutorPoolTest1.main(ExecutorPoolTest1.java:25)
我是ool-1-thad-7线程,正在执行11的任务!
我是ool-1-thad-4线程,正在执行13的任务!
我是ool-1-thad-10线程,正在执行12的任务!
我是ool-1-thad-5线程,正在执行14的任务!
我是ool-1-thad-8线程,正在执行15的任务!通过上面这个方法,我们可以看出,如果线程数量正好是15(for循环次数)个线程,则线程池中可以总线程数量为10个,加上5个阻塞队列,正好可以执行15个任务,总共创建10个线程!如果切换异常执行方式,也就是最后一个参数,会有什么区别呢??请自行尝试!