成功-最佳实践-类型-概念-一文带你彻底弄懂线程池-好处 (成功的最佳途径)
一、前言
只管对线程的创立、终止、期待、通知、销毁、同步等性能提供了很多的支持,但是从操作系统角度来说,频繁的创立线程和销毁线程,其实是须要少量的期间和资源的。
例如,当有多个义务同时须要处置的时刻,一个义务对应一个线程来口头,以此来优化义务的口头效率,模型图如下:
图片
假设义务数十分少,这种形式倒疑问不大,但是假设义务数十分的多,或许就会存在很大的疑问:
假设把很多义务让一组线程来口头,而不是一个义务对应一个新线程,这种经过接受义务并启动散发处置的就是线程池。
图片
线程池外部保养了若干个线程,当没有义务的时刻,这些线程都处于期待形态;当有新的义务出去时,就调配一个闲暇线程口头;当一切线程都处于忙碌形态时,新义务要么放入队列中期待,要么参与一个新线程启动处置,要么间接拒绝。
很显然,这种经过线程池来口头多义务的思绪,好处显著:
关于这一点,咱们可以看一个繁难的对比示例。
/***经常使用一个义务对应一个线程来口头*@paramargs*/publicstaticvoidmn(String[]args){longstartTime=System.currentTimeMillis();finalRandomrandom=newRandom();List<Integer>list=newCopyOnWriteArrayList<>();//一个义务对应一个线程,经常使用20000个线程口头义务for(inti=0;i<20000;i++){newThread(newRunnable(){@Overridepublicvoidrun(){list.add(random.nextInt(100));}}).start();}//期待义务口头终了while(true){if(list.size()>=20000){break;}}System.out.println("一个义务对应一个线程,口头耗时:"+(System.currentTimeMillis()-startTime)+"ms");}
/***经常使用线程池启动口头义务*@paramargs*/publicstaticvoidmain(String[]args){longstartTime=System.currentTimeMillis();finalRandomrandom=newRandom();List<Integer>list=newCopyOnWriteArrayList<>();//经常使用线程池启动口头义务,自动4个线程ThreadPoolExecutorexecutor=newThreadPoolExecutor(4,4,60,TimeUnit.SECONDS,newLinkedBlockingQueue<Runnable>(20000));for(inti=0;i<20000;i++){//提交义务executor.submit(newRunnable(){@Overridepublicvoidrun(){list.add(random.nextInt(100));}});}//期待义务口头终了while(true){if(list.size()>=20000){break;}}System.out.println("经常使用线程池,口头耗时:"+(System.currentTimeMillis()-startTime)+"ms");//封锁线程池executor.shutdown();}
两者口头耗时状况对比,如下:
一个义务对应一个线程,口头耗时:3073ms---------------------------经常使用线程池,口头耗时:578ms
从结果上可以看出,雷同的义务数,驳回线程池和不驳回线程池,口头耗时差距十分显著,一个义务对应一个新的线程来口头,反而效率不如驳回4个线程的线程池口头的快。
为什么会发生这种现象,上方咱们就一同来聊聊线程池。
二、线程池概述
站在专业的角度讲,线程池其实是一种应用池化思维来成功线程治理的技术,它将线程的创立和义务的口头启动解耦,同时复用曾经创立的线程来降落频繁创立和销毁线程所带来的资源消耗。经过正当的参数设置,可以成功更低的系统资源经常使用率、更高的义务并发口头效率。
在Java中,线程池最顶级的接口是Executor,名下的成功类相关图如下:
图片
关键接口和成功类,相关的形容如下:
整个相关图中,其中ThreadPoolExecutor是线程池最外围的成功类,开发者可以经常使用它来创立线程池。
2.1、ThreadPoolExecutor结构方法
ThreadPoolExecutor类的完整结构方法一共有七个参数,了解这些参数的性能对经常使用好线程池至关关键,完整的结构方法外围源码如下:
publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueue<Runnable>workQueue,ThreadFactorythreadFactory,RejectedExecutionHandlerhandler)
各个参数的解读如下:
2.2、ThreadPoolExecutor口头流程
创立完线程池之后就可以提交义务了,当有新的义务出去时,线程池就会上班并调配线程去口头义务。
ThreadPoolExecutor的典型用法如下:
//创立固定大小的线程池ThreadPoolExecutorexecutor=newThreadPoolExecutor(4,4,60,TimeUnit.SECONDS,newLinkedBlockingQueue<Runnable>(100));//提交义务executor.execute(task1);executor.execute(task2);executor.execute(task3);...
针对义务的提交方式,ThreadPoolExecutor还提供了两种方法。
ThreadPoolExecutor口头提交的义务流程只管比拟复杂,但是经过对源码的剖析,大抵的义务口头流程,可以用如下图来概括。
整个口头流程,大体步骤如下:
咱们再回头来看上文提到的ThreadPoolExecutor结构方法中的七个参数,这些参数会间接影响线程的口头状况,各个参数的变动状况,可以用如下几点来概括:
ThreadPoolExecutor口头义务的局部外围源码如下!
2.2.1、execute提交义务
publicvoidexecute(Runnablecommand){if(command==null)thrownewNullPointerException();intc=ctl.get();//上班线程数量<corePoolSize,间接创立线程口头义务if(workerCountOf(c)<corePoolSize){if(addWorker(command,true))return;c=ctl.get();}//上班线程数量>=corePoolSize,将义务参与至阻塞队列中if(isRunning(c)&&workQueue.offer(command)){intrecheck=ctl.get();//往阻塞队列中参与义务的时刻,假设线程池非运转形态,将义务remove,并口头拒绝战略if(!isRunning(recheck)&&remove(command))reject(command);elseif(workerCountOf(recheck)==0)addWorker(null,false);}//阻塞队列已满,尝试参与新的线程去口头,假设上班线程数量>=maximumPoolSize,口头拒绝战略elseif(!addWorker(command,false))reject(command);}
2.2.2、addWorker创立线程参与线程池
privatebooleanaddWorker(RunnablefirstTask,booleancore){retry:for(;;){intc=ctl.get();intrs=runStateOf(c);//线程池形态处于非RUNNING形态,参与worker失败if(rs>=SHUTDOWN&&!(rs==SHUTDOWN&&firstTask==null&&!workQueue.isEmpty()))returnfalse;//判别线程池中线程数量大于等于该线程池准许的最大线程数量,假设大于则worker失败,反之cas降级线程池中的线程数for(;;){intwc=workerCountOf(c);if(wc>=CAPACITY||wc>=(core?corePoolSize:maximumPoolSize))returnfalse;if(compareAndIncrementWorkerCount(c))breakretry;c=ctl.get();//Re-readctlif(runStateOf(c)!=rs)continueretry;//elseCASfailedduetoworkerCountchange;retryinnerloop}}booleanworkerStarted=false;booleanworkerAdded=false;Workerw=null;try{//创立上班线程w=newWorker(firstTask);finalThreadt=w.thread;if(t!=null){finalReentrantLockmainLock=this.mainLock;mainLock.lock();try{intrs=runStateOf(ctl.get());if(rs<SHUTDOWN||(rs==SHUTDOWN&&firstTask==null)){//假设线程池处于RUNNING形态并且线程曾经启动,则抛出线程意外启动if(t.isAlive())thrownewIllegalThreadStateException();//将线程参与已创立的上班线程汇合,降级用于追踪线程池中线程数量largestPoolSize字段workers.add(w);ints=workers.size();if(s>largestPoolSize)largestPoolSize=s;workerAdded=true;}}finally{mainLock.unlock();}if(workerAdded){//启动线程口头义务t.start();workerStarted=true;}}}finally{if(!workerStarted)addWorkerFailed(w);}returnworkerStarted;}
2.2.3、runWorker口头义务
finalvoidrunWorker(Workerw){//失掉口头义务线程Threadwt=Thread.currentThread();//失掉口头义务Runnabletask=w.firstTask;//将worker中的义务置空w.firstTask=null;w.unlock();//allowinterruptsbooleancompletedAbruptly=true;try{//从以后上班线程种失掉义务,或许循环从阻塞义务队列中失掉义务while(task!=null||(task=getTask())!=null){w.lock();//双重审核线程池能否正在中止,假设线程池中止,并且以后线程能够终止,则终止线程if((runStateAtLeast(ctl.get(),STOP)||(Thread.interrupted()&&runStateAtLeast(ctl.get(),STOP)))&&!wt.isInterrupted())wt.interrupt();try{//前置口头义务钩子函数beforeExecute(wt,task);Throwablethrown=null;try{//口头以后义务task.run();}catch(RuntimeExceptionx){thrown=x;throwx;}catch(Errorx){thrown=x;throwx;}catch(Throwablex){thrown=x;thrownewError(x);}finally{//后置口头义务钩子函数afterExecute(task,thrown);}}finally{task=null;w.completedTasks++;w.unlock();}}completedAbruptly=false;}finally{//回收线程processWorkerExit(w,completedAbruptly);}}
2.2.4、reject口头拒绝战略
finalvoidreject(Runnablecommand){//口头拒绝战略handler.rejectedExecution(command,this);}
当线程池中的线程数大于等于maximumPoolSize,并且workQueue已满,新义务会被拒绝,经常使用RejectedExecutionHandler接口的rejectedExecution()方法来处置被拒绝的义务。
![线程池](https://tyye.cn/zb_users/theme/tpure/style/images/lazyload.png)
线程池提供了四种拒绝战略成功类来拒绝义务,详细如下:
类 |
形容 |
AbortPolicy |
间接抛出一个RejectedExecutionException,这也是JDK自动的拒绝战略 |
DiscardPolicy |
什么也不做,间接摈弃义务 |
DiscardOldestPolicy |
将阻塞队列中的义务移除出来,而后口头以后义务 |
CallerRunsPolicy |
尝试间接运转被拒绝的义务,假设线程池曾经被封锁了,义务就被摈弃了 |
2.3、ThreadPoolExecutor线程池形态
咱们知道Java种的线程一共6种形态,其实线程池也有形态。
由于线程池也是异步口头的,有的义务正在口头,有的义务存储在义务队列中,有的线程处于上班形态,有的线程处于闲暇形态期待回收,为了愈加精细化的治理线程池,线程池也设计了5中形态,局部外围源码如下:
publicclassThreadPoolExecutorextendsAbstractExecutorService{//线程池线程数的bit数privatestaticfinalintCOUNT_BITS=Integer.SIZE-3;//线程池形态privatestaticfinalintRUNNING=-1<<COUNT_BITS;privatestaticfinalintSHUTDOWN=0<<COUNT_BITS;privatestaticfinalintSTOP=1<<COUNT_BITS;privatestaticfinalintTIDYING=2<<COUNT_BITS;privatestaticfinalintTERMINATED=3<<COUNT_BITS;}
其中的形态流程,可以用如下图来形容!
这几个形态的转化相关,可以用如下几个步骤来概括:
三、线程池运行
正如文章的扫尾所引见的,经常使用线程池的方式,理论可以用如下几个步骤来概括:
//1.创立固定大小为4的线程数、闲暇线程的存定期间为15秒、阻塞义务队列的下限为1000的线程池完整示例ThreadPoolExecutorexecutor=newThreadPoolExecutor(4,4,15,TimeUnit.SECONDS,newLinkedBlockingQueue<>(1000),Executors.defaultThreadFactory(),newThreadPoolExecutor.AbortPolicy());//2.提交义务executor.submit(task1);executor.submit(task2);executor.submit(task3);...//3.经常使用终了之后,可以手动封锁线程池executor.shutdown();
正如上文所说,其中execute()和submit()方法都可以用来提交义务,稍有不同的是:submit()方法同时还支持失掉义务口头终了的前往结果。
针对线程池的经常使用,Java还提供了Executors工具类,开发者可以经过此工具,极速创立不同类型的线程池。
上方咱们一同来看下Executors为用户提供的几种创立线程池的方法。
3.1、newSingleThreadExecutor
newSingleThreadExecutor()方法示意创立一个复线程的线程池,外围源码如下:
publicstaticExecutorServicenewSingleThreadExecutor(){returnnewFinalizableDelegatedExecutorService(newThreadPoolExecutor(1,1,0L,TimeUnit.MILLISECONDS,newLinkedBlockingQueue<Runnable>()));}
从结构参数上可以很明晰的看到,线程池中的线程数为1,不会被线程池智能回收,workQueue选用的是无界的LinkedBlockingQueue阻塞队列,不论来多少义务存入阻塞队列中,前面一个义务口头终了,再口头队列中的残余义务。
繁难运行示例如下:
publicstaticvoidmain(String[]args){longstartTime=System.currentTimeMillis();finalRandomrandom=newRandom();List<Integer>list=newCopyOnWriteArrayList<>();//创立一个复线程线程池ExecutorServiceexecutor=Executors.newSingleThreadExecutor();for(inti=0;i<10;i++){executor.submit(newRunnable(){@Overridepublicvoidrun(){list.add(random.nextInt(100));System.out.println("threadname:"+Thread.currentThread().getName());}});}while(true){if(list.size()>=10){break;}}System.out.println("口头耗时:"+(System.currentTimeMillis()-startTime)+"ms");//封锁线程池executor.shutdown();}
运转结果如下:
threadname:pool-1-thread-1threadname:pool-1-thread-1threadname:pool-1-thread-1threadname:pool-1-thread-1threadname:pool-1-thread-1threadname:pool-1-thread-1threadname:pool-1-thread-1threadname:pool-1-thread-1threadname:pool-1-thread-1threadname:pool-1-thread-1口头耗时:13ms
3.2、newFixedThreadPool
newFixedThreadPool()方法示意创立一个固定大小线程数的线程池,外围源码如下:
publicstaticExecutorServicenewFixedThreadPool(intnThreads){returnnewThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.MILLISECONDS,newLinkedBlockingQueue<Runnable>());}
固定大小的线程池和复线程的线程池有必由之路之处,无非是让线程池中能运转的线程数量支持手动指定。
繁难运行示例如下:
publicstaticvoidmain(String[]args){longstartTime=System.currentTimeMillis();finalRandomrandom=newRandom();List<Integer>list=newCopyOnWriteArrayList<>();//创立固定大小线程数为3的线程池ExecutorServiceexecutor=Executors.newFixedThreadPool(3);for(inti=0;i<10;i++){executor.submit(newRunnable(){@Overridepublicvoidrun(){list.add(random.nextInt(100));System.out.println("threadname:"+Thread.currentThread().getName());}});}while(true){if(list.size()>=10){break;}}System.out.println("口头耗时:"+(System.currentTimeMillis()-startTime)+"ms");//封锁线程池executor.shutdown();}
运转结果如下:
threadname:pool-1-thread-2threadname:pool-1-thread-1threadname:pool-1-thread-3threadname:pool-1-thread-3threadname:pool-1-thread-3threadname:pool-1-thread-1threadname:pool-1-thread-3threadname:pool-1-thread-2threadname:pool-1-thread-2threadname:pool-1-thread-1口头耗时:10ms
3.3、newCachedThreadPool
newCachedThreadPool()方法示意创立一个可缓存的无界限程池,外围源码如下:
publicstaticExecutorServicenewCachedThreadPool(){returnnewThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,newSynchronousQueue<Runnable>());}
从结构参数上可以看出,线程池中的最大线程数为Integer.MAX_VALUE,也就是Integer的最大值,workQueue选用的是SynchronousQueue阻塞队列,这个阻塞队列不像LinkedBlockingQueue,它没有容量,只担任做暂时义务缓存,假设有义务出去立刻会被口头。
也就是说,只需参与出来了义务,线程就会立刻去口头,当义务超越线程池的线程数则创立新的线程去口头,线程数量的最大上线为Integer.MAX_VALUE,当线程池中的线程闲暇期间超越60s,则会智能回收该线程。
繁难运行示例如下:
publicstaticvoidmain(String[]args){longstartTime=System.currentTimeMillis();finalRandomrandom=newRandom();List<Integer>list=newCopyOnWriteArrayList<>();//创立可缓存的无界限程池ExecutorServiceexecutor=Executors.newCachedThreadPool();for(inti=0;i<10;i++){executor.submit(newRunnable(){@Overridepublicvoidrun(){list.add(random.nextInt(100));System.out.println("threadname:"+Thread.currentThread().getName());}});}while(true){if(list.size()>=10){break;}}System.out.println("口头耗时:"+(System.currentTimeMillis()-startTime)+"ms");//封锁线程池executor.shutdown();}
运转结果如下:
threadname:pool-1-thread-1threadname:pool-1-thread-2threadname:pool-1-thread-3threadname:pool-1-thread-4threadname:pool-1-thread-3threadname:pool-1-thread-2threadname:pool-1-thread-1threadname:pool-1-thread-4threadname:pool-1-thread-4threadname:pool-1-thread-4口头耗时:13ms
3.4、newScheduledThreadPool
newScheduledThreadPool()方法示意创立周期性的线程池,可以指定线程池中的外围线程数,支持定时及周期性义务的口头,外围源码如下:
publicScheduledThreadPoolExecutor(intcorePoolSize){super(corePoolSize,Integer.MAX_VALUE,0,NANOSECONDS,newDelayedWorkQueue());}
从结构参数上可以看出,线程池支持指定外围线程数,最大线程数为Integer.MAX_VALUE,workQueue选用的是DelayedWorkQueue提前阻塞队列,这个阻塞队列支持义务提前消费,新参与的义务不会立刻被口头,只要期间到期之后才会被取出;当非外围线程处于闲暇形态时,会立刻启动收回。
ScheduledExecutorService支持三种类型的定时调度方法,区分如下:
上方咱们一同来看看它们的运行方式。
3.4.1、schedule方法经常使用示例
SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddhh:mm:ss");//创立线程数量为2的定时调度线程池ScheduledExecutorServiceexecutor=Executors.newScheduledThreadPool(2);System.out.println(sdf.format(newDate())+"预备启动");//定时口头一次性的义务,提前1s后口头executor.schedule(newRunnable(){@Overridepublicvoidrun(){System.out.println(sdf.format(newDate())+"threadname:"+Thread.currentThread().getName()+",schedule");}},1,TimeUnit.SECONDS);
输入结果:
2023-11-1701:41:12预备启动2023-11-1701:41:13threadname:pool-1-thread-1,schedule
3.4.2、scheduleAtFixedRate方法经常使用示例
SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddhh:mm:ss");//创立线程数量为2的定时调度线程池ScheduledExecutorServiceexecutor=Executors.newScheduledThreadPool(2);System.out.println(sdf.format(newDate())+"预备启动");//周期性地口头义务,第一个义务提前1s后口头,之后每隔2s周期性口头义务,须要期待上一次性的义务口头终了才口头下一个executor.scheduleAtFixedRate(newRunnable(){@Overridepublicvoidrun(){System.out.println(sdf.format(newDate())+"threadname:"+Thread.currentThread().getName()+"begin");try{Thread.sleep(3000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(sdf.format(newDate())+"threadname:"+Thread.currentThread().getName()+"end");}},1,2,TimeUnit.SECONDS);
输入结果:
2023-11-1702:00:44预备启动2023-11-1702:00:45threadname:pool-1-thread-1begin2023-11-1702:00:48threadname:pool-1-thread-1end2023-11-1702:00:48threadname:pool-1-thread-1begin2023-11-1702:00:51threadname:pool-1-thread-1end2023-11-1702:00:51threadname:pool-1-thread-1begin2023-11-1702:00:54threadname:pool-1-thread-1end
3.4.3、scheduleWithFixedDelay方法经常使用示例
SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-ddhh:mm:ss");//创立线程数量为2的定时调度线程池ScheduledExecutorServiceexecutor=Executors.newScheduledThreadPool(2);System.out.println(sdf.format(newDate())+"预备启动");//周期性地口头义务,第一个义务提前1s后口头,之后上一个义务口头终了之后,提前2秒再口头下一个义务executor.scheduleWithFixedDelay(newRunnable(){@Overridepublicvoidrun(){System.out.println(sdf.format(newDate())+"threadname:"+Thread.currentThread().getName()+"begin");try{Thread.sleep(3000);}catch(InterruptedExceptione){e.printStackTrace();}System.out.println(sdf.format(newDate())+"threadname:"+Thread.currentThread().getName()+"end");}},1,2,TimeUnit.SECONDS);
输入结果:
2023-11-1701:53:26预备启动2023-11-1701:53:27threadname:pool-1-thread-1begin2023-11-1701:53:30threadname:pool-1-thread-1end2023-11-1701:53:32threadname:pool-1-thread-1begin2023-11-1701:53:35threadname:pool-1-thread-1end2023-11-1701:53:37threadname:pool-1-thread-1begin2023-11-1701:53:40threadname:pool-1-thread-1end
3.5、工厂方法小结
从以上的引见中,咱们可以对这四种线程池的参数做一个汇总,内容如下表:
工厂方法 |
corePoolSize |
maximumPoolSize |
keepAliveTime |
newSingleThreadExecutor |
LinkedBlockingQueue |
||
newFixedThreadPool |
LinkedBlockingQueue |
||
newCachedThreadPool |
Integer.MAX_VALUE |
SynchronousQueue |
|
newScheduledThreadPool |
corePoolSize |
Integer.MAX_VALUE |
DelayedWorkQueue |
这四个线程池,关键的区别在于:corePoolSize、maximumPoolSize、keepAliveTime、workQueue这四个参数,其中线程工厂为自动类DefaultThreadFactory,线程饱和的拒绝战略为自动类AbortPolicy。
四、小结
联合以上的剖析,最后咱们再来总结一下。
关于线程池的经常使用,不太倡导驳回Executors工具去创立,尽量经过ThreadPoolExecutor的结构方法来创立,要素在于:无利于规避资源耗尽的危险;同时倡导开发者手动设定义务队列的下限,防止服务发生OOM。
只管Executors工具提供了四种创立线程池的方法,能协助开发者省去繁琐的参数性能,但是newSingleThreadExecutor和newFixedThreadPool方法创立的线程池,义务队列下限为Integer.MAX_VALUE,这象征着可以有限提交义务,这在高并发的环境下,系统或许会发生OOM,造成整个线程池无法用;其次newCachedThreadPool方法也存在雷同的疑问,有限的创立线程或许会给系统带来更多的资源消耗。
其次,创立线程池的时刻应该尽量给线程定义一个详细的业务名字前缀,繁难定位疑问,不同类型的业务尽量经常使用不同的线程池来成功。
例如可以经常使用guava包,创立自定义的线程工厂。
ThreadFactorythreadFactory=newThreadFactoryBuilder().setNameFormat(threadNamePrefix+"-%d").setDaemon(true).build();
当然,你也可以自行成功一个线程工厂,须要承袭ThreadFactory接口,案例如下:
importjava.util.concurrent.Executors;importjava.util.concurrent.ThreadFactory;importjava.util.concurrent.atomic.AtomicInteger;/***线程工厂,它设置线程称号,无利于咱们定位疑问。*/publicfinalclassNamingThreadFactoryimplementsThreadFactory{privatefinalAtomicIntegerthreadNum=newAtomicInteger();privatefinalThreadFactorydelegate;privatefinalStringname;/***创立一个带名字的线程池消费工厂*/publicNamingThreadFactory(ThreadFactorydelegate,Stringname){this.delegate=delegate;this.name=name;}@OverridepublicThreadnewThread(Runnabler){Threadt=delegate.newThread(r);t.setName(name+"-"+threadNum.incrementAndGet());returnt;}}
创立一个线程称号以order扫尾的线程工厂。
NamingThreadFactorythreadFactory=newNamingThreadFactory(Executors.defaultThreadFactory(),"order");
最后,再来说说关于线程池中线程数,如何正当设定的疑问?
那如何判别以后是CPU密集型义务还是I/O密集型义务呢?
最繁难的方法就是:假设以后义务触及到网络读取,文件读取等,这类都是IO密集型义务,除此之外,可以看成是CPU密集型义务。
本文篇幅比拟长,不免有形容不对的中央,欢迎大家留言指出!
五、参考
java学习作为一名java初学者,如何快速学习j
那首先来了解一下什么是java:
Java是SUN(Stanford University Network,斯坦福大学网络公司)1995年推出的一门高级编程语言,是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。
那么为什么要使用这个语言呢,Java语言的特点跨平台性,通过Java语言编写的应用程序在不同的系统平台上都可以运行。原理是只要在需要运行java应用程序的操作系统上,先安装一个Java虚拟机(JVM Java Virtual Machine)即可。由JVM来负责Java程序在该系统中的运行。
下面对java学习进行一次史无前例的剖析,细致的讲解零基础的人怎么学习Java。先看下Java在基础阶段的知识点路线图。
内容多吗?不要被吓到了,知识点剖析的比较细,所以看着比较多。不要害怕学不会,经过下面的详解,你就会明白这些知识点都是干嘛的了。
入门阶段,主要是培养Java语言的编程思想。了解Java语言的语法,书写规范等,掌握Eclipse、MyEclipse等开发工具,编写Java代码的能力。学完这个阶段你应该可进行小型应用程序开发并且可以对数据库进行基本的增删改查管理。注意:此阶段知识点的学习,会有真实的项目进行驱动学习,让你轻松理解各知识点。
1计算机基础知识
针对零基础学习的人,从对计算机操作等知识的了解,延伸到Java语言的发展与开发工具的使用上。主要是让你知道怎样执行计算机命令,认识Java这门语言,感受编程语言Java怎么开发程序。
1) 计算机基础
让零基础学习的人先了解计算机相关知识,进而再去了解Java语言。
2) DOS常用命令
了解什么是DOS,并掌握DOS控制台的打开方式,同时熟悉常用的DOS命令,例如:盘符切换、进入指定目录、删除文件等,完成使用DOS命令对计算机进行操作和控制。
3) Java概述
了解Java语言的发展史、Java语言平台版本、Java语言的特点,以及JRE与JDK。JRE是Java的运行环境,JDK是Java开发工具包,它包含了Java的开发工具以及JRE。所以安装了JDK就不用再单独安装JRE了。
4) JDK环境安装配置
了解了什么是JDK,以及JDK的重要性,下一步我们就来学习如何安装和配置JDK环境。在安装JDK之前,我们首先需要下载JDK,针对不同的系统,我们需要下载不用版本的JDK。
5) 环境变量配置
了解path、classpath环境变量,理解path变量和classpath变量的作用,并掌握path变量和classpath变量的配置方式。
6) Java程序入门
当JDK、环境变量配置完毕,我们就可以开始 编写Java程序。编写Java程序可以使用如下几种工具:notepad(微软操作系统自带)、Editplus、Notepad++、Eclipse、MyEclipse,sublime等等。
IntelliJ IDEA工具的使用(重点)
2编程基础
此模块学习是让你了解编程的具体流程,学习Java基础语法的格式等。具体要掌握不同数据类型的变量定义与使用,掌握不同运算符的运算规则,掌握流程控制语句的执行流程,编写方法的声明与调用,创建数组并访问数组元素等知识。
1) 注释
在程序开发工程中,用于解释和说明程序的文字我们称之为注释,Java中的注释分为以下几种:单行注释、多行注释、文档注释。
2) 关键字
了解Java关键字的含义及特点,掌握关键字使用的注意事项。
3) 标识符
了解什么是标识符,标识符的组成规则,以及标识符使用时的注意事项。
4) 常量与变量
理解常量与变量的含义,并掌握常量与变量的区别、变量的定义格式以及变量的赋值。
5) 数据类型
掌握Java语言的数据类型,如基本数据类型:byte、short、int、long、float、double、char、boolean,以及引用类型:类、接口、数组。
6) 运算符
熟练掌握Java中的运算符:算术运算符、赋值运算符、比较运算符、逻辑运算符、位运算符、三目运算符。
7) 流程控制语句
了解什么是流程控制语句,掌握以下流程控制语句:顺序结构、选择结构、循环结构,并能够通过流程控制语句实现特定的功能。
8) 方法
掌握方法的定义及格式,并能正确的调用方法,理解方法的调用过程,同时清楚方法的注意事项;掌握方法重载及其特点。
9) 数组
了解数组的概念,掌握数组的定义格式、静态初始化、动态初始化,并能够理解Java中数组的内存图解。熟练掌握数组的遍历、获取最值、数组元素逆序、数组元素查找、数组排序和二分查找,以及二维数组的定义格式及初始化。
3面向对象
现实世界中,随处可见的一种事物就是对象,对象是事物存在的实体,如人类、书桌、计算机、高楼大厦等。人类解决问题的方式总是将复杂的事物简单化,于是就会思考这些对象都是由哪些部分组成的。通常都会将对象划分为两个部分,即动态部分与静态部分。静态部分,顾名思义就是不能动的部分,这个部分被称为“属性”,任何对象都会具备其自身属性,如一个人,它包括高矮、胖瘦、性别、年龄等属性。然而具有这些属性的人会执行哪些动作也是一个值得探讨的部分,这个人可以哭泣、微笑、说话、行走,这些是这个人具备的行为(动态部分),人类通过探讨对象的属性和观察对象的行为了解对象。
1) 面向对象思想
了解面向过程编程思想,能够通过案例理解Java的面向对象编程思想,了解面向对象开发、设计、特征。
2) 类与对象
了解什么是类,什么是对象,并理解类与对象之间的关系;熟练掌握类的定义、对象内存图等。
3) 成员变量和局部变量
了解什么是成员变量,什么是局部变量,以及从他们在类中的位置、内存中的位置、生命周期、初始化值等方面掌握他们的区别。
4) 匿名对象
了解什么是匿名对象,掌握匿名对象的两种使用情况。
5) 封装
清楚的了解什么是封装,并能够理解封装的优点与缺点,同时掌握封装的原则。
6) this关键字
掌握this关键字的含义与使用。
7) 构造方法
了解什么是构造方法,构造方法的作用,以及与构造方法相关的注意事项。
8) 继承
理解什么是继承,继承的好处以及java中继承的特点和注意事项,继承中成员变量的关系、构造方法的关系、成员方法的关系,方法重写与方法重载的区别。
9) 多态
理解什么是多态,掌握多态案例及成员访问的特点,多态的优点和缺点,多态中的转型问题。
10) 抽象类
了解什么是抽象类,抽象类的特点,抽象类成员的特点。
11) 接口
了解什么是接口,接口的特点,接口成员的特点,类与类、类与接口的关系,以及抽象类与接口的区别。
12) 内部类
什么是内部类,内部类的访问特点,内部类的位置,什么是成员内部类、局部内部类、匿名内部类,以及匿名内部类在开发中的使用。
4、常用类
类库就是Java API(Application Programming Interface,应用程序接口),是系统提供的已实现的标准类的集合。在程序设计中,合理和充分利用类库提供的类和接口,不仅可以完成字符串处理、绘图、网络应用、数学计算等多方面的工作,而且可以大大提高编程效率,使程序简练、易懂。
学习内容:掌握Object类、Scanner类、String类、StringBuffer类、StringBuilder类、Arrays类、基本包装类、正则表达式、Math类、Random类、System类、Date类、DateFormate类、Calendar类,及其常用方法。
5、集合
集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引用。简单一点就是说,集合是存放数据的容器。
学习内容:什么是集合?数组与集合有什么区别,集合类的特点,掌握Collection接口、Iterator接口、List接口、ListIterator接口、ArrayList类、Vector类、LinkedList类、泛型、Set接口、HashSet类、Map接口、HashMap类、LinkedHashMap类等。
IO(Input/Output)是计算机输出/输出的接口。Java的核心库提供了全面的IO接口,包括:文件读写,标准设备输出等等。Java中IO是以流为基础进行输入输出的,所有数据被串行化写入输出流,或者从输入流读入。
1) 异常
了解什么是异常,异常的由来,常见的异常,异常的分类,掌握jvm对异常的默认处理方案,异常的处理方案:try…catch…finally、throws,什么是编译时异常,什么是运行时异常,掌握它们两的区别,throws关键字、throw关键字,以及这两个关键字的区别,熟练掌握自定义异常,异常注意事项。
2) File类
了解什么是File类,File类的用途,掌握File类的方法:createNewFile()、mkdir()、mkdirs()、delete()、renameTo(File dest)、isDirectory()、isFile()、exists()、等方法,以及File类的基本获取功能方法和高级获取功能方法。
3) IO流
了解什么是IO流,IO流的用途;熟练掌握输入流、输出流、字符流、字节流、IO流的常用基类;如何使用字节流读写数据、复制数据;什么是字节缓冲流,如何使用字节缓冲流读写数据;什么是转换流,如何使用转换流更加高效的读写数据,内存操作流、打印流、标准输入输出流、序列化流、Properties集合。
4) IO流练习
(1) 复制文本文件;
(2) 复制图片;
(3) 把ArrayList集合中的字符串数据存储到文本文件;
(4) 从文本文件中读取数据(每一行为一个字符串数据)到集合中,并遍历集合;
(5) 复制单极文件夹;
(6) 复制单极文件夹中指定文件并修改文件名称;
(7) 复制多极文件夹;
(8) 已知文件中有这样的一个字符串:“hcexfgijkamdnoqrzstuvwybpl”;
(9) 请编写程序读取数据内容,把数据排序后写入中;
(10) 获取每次读取数据的行号;
(11) 登录注册IO版。
7、多线程
是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理(Chip-level multithreading)或同时多线程(Simultaneous multithreading)处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理(Multithreading)”。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程(台湾译作“执行绪”),进而提升整体处理性能。
1) 多线程
了解什么是线程,什么是多线程,理解java程序的运行原理,掌握多线程的实现方案,如何获取和设置线程名称、线程的生命周期、如何解决线程安全问题、线程同步、Lock锁、死锁问题、线程间通信、线程的状态及状态转换、线程池。
2) 多线程面试题
(1) 多线程有几种实现方案,分别是哪几种?
(2) 同步有几种方式,分别是什么?
(3) 启动一个线程是run()还是start()?它们的区别?
(4) sleep()和wait()方法的区别;
(5) 为什么wait(),notify(),notifyAll()等方法都定义在Object类中;
(6) 线程的生命周期图。
3) 设计模式
了解什么是设计模式,设计模式的分类,熟练掌握单例设计模式(懒汉式、饿汉式)。
8 、网络编程
网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。
9、反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java新特性:(重点及理解)
Java7特性重点:
switch支持String、泛型自动类型推断
理解:可catch多个异常,异常之间使用“|”分隔
Java8特性重点:Lambda表达式、接口中的默认方法和静态方法、新的Date API。
大家凑合着看看吧。
视频教程你可以去B站上看一下老杜的159集的视频教程,这套视频教程在B站上的播放量是140多万,你说这套视频教程好不好呢?
7000字+24张图带你彻底弄懂线程池
深入探索线程池的秘密,让我们一起揭开它那7000字的神秘面纱,通过24幅生动图解,彻底理解线程池的运作机制。首先,让我们触及核心:线程池原理,它巧妙地运用池化技术,让资源得以高效复用,降低创建与销毁的开销,提升性能。
Java的世界里,线程池的实现由ThreadPoolExecutor来塑造,通过一系列关键参数如核心线程数、最大线程数、线程存活时间和队列类型,精细调控线程的行为。默认的AbortPolicy策略处理任务拒绝,但你也可以自定义RejectedExecutionHandler,赋予它更灵活的策略。
执行任务的旅程开始于execute方法:当任务需求超过核心线程数时,非核心线程会加入战局。但若线程池满载,任务的接纳与否则由拒绝策略决定。线程池的复用机制,通过Worker对象和AQS(AbstractQueuedSynchronizer)的巧妙设计,确保任务持续流转。
线程获取任务的方式并不简单,getTask方法巧妙地引入超时机制,线程要么立即执行,要么在指定时间内等待获取。线程池的运行机制则关乎keepAliveTime的设定,它能促使空闲线程优雅退出,同时利用poll方法的超时特性,保证线程池的稳定。
线程池的状态变化丰富多样,从RUNNING到TERMINATED,每个阶段都有其独特的意义。关闭线程池的方式有多种,shutdown暂停接收新任务,而shutdownNow则会立即中断所有任务,不过它不会处理队列中的遗留任务。
监控线程池的健康状态是至关重要的,getCompletedTaskCount、getLargestPoolSize、getActiveCount和getPoolSize这些工具方法,为我们提供了实时的性能指标。但别忘了,Executors工具类能简化线程池创建,包括固定线程数和单线程池等类型,只是要谨慎选择,以避免潜在的内存溢出或线程过多的问题。
线程池的应用场景广泛,它在异步处理和多线程任务中大放异彩,避免了频繁创建与销毁线程带来的资源浪费。在实际项目中,合理设置线程数至关重要。例如,CPU密集型任务可设置为CPU核心数+1,而IO密集型任务则可能需要2倍于CPU核心数的线程。同时,定制化的线程工厂和有界队列能帮助我们更好地控制任务数据规模,预防内存溢出的风险。
最后,如果你对线程池的更多细节和实战案例感兴趣,记得关注我们的公众号“三友的java日记”,那里有更多的内容等待你探索,同时,你的点赞与转发是对我们最大的支持。让我们一起在代码的世界里,体验线程池的魔力吧!
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。