当前位置: 首页 > 科技观察

学习Java多线程的一个线程

时间:2023-03-16 20:59:02 科技观察

,转载请联系我。我是开发者FTD公众号。我们在学习软件开发的时候,多线程和高并发是必不可少的知识点,也是面试的时候会问到的内容。为了让大家对多线程和高并发编程有一个清晰的认识,我们特意组织了一个专栏来介绍一下,希望对大家有所帮助。线程简介线程是程序运行的基本执行单元。操作系统(不包括单线程操作系统,如微软早期的DOS)在执行一个程序时,会在系统中建立一个进程,在这个进程中,至少要建立一个线程(这个线程称为main线)。线程)作为该程序运行的入口点。因此,在操作系统中运行的任何程序都至少有一个主线程。进程和线程是现代操作系统中两种基本的运行模型。操作系统中可以有多个进程,这些进程包括系统进程(操作系统建立的进程)和用户进程(用户程序建立的进程);一个进程中可以有一个或多个线程。进程之间不共享内存,也就是说系统中的进程运行在自己独立的内存空间中。一个进程中的线程可以共享系统分配给该进程的内存空间。在进一步深入之前,让我们了解一些基本概念,以帮助您更快地理解。基本概念进程进程是操作系统中正在执行的不同应用程序,例如:我们可以同时打开微信和QQ,甚至更多的程序。在操作系统中运行的程序是一个进程。进程是执行程序的执行进程。它是一个动态的概念系统资源分配单元。通常,一个进程可以包含多个线程。当然,一个进程中至少有一个线程,否则就没有存在的意义。线程是CPU调度执行的单位线程。线程是一个应用进程中不同的执行路径。例如:我们的WEB服务器可以同时为多个用户提供请求服务。一个进程是不活动的,一个进程从不执行任何东西,它只是线程的容器。线程总是在进程的上下文中创建,并且它在整个生命周期中都存在于该进程中。在一个进程中,如果创建了多个线程,线程的运行是由调度器来安排和调度的。调度器与操作系统密切相关,顺序无法人为干预。多线程和多线程有多个执行路径。“主线程和子线程并行交替执行”(普通方法只有主线程一条路径)。在对同一个资源进行操作时,会出现资源抢夺的问题。这时候就需要加入并发控制。一个Java应用程序至少有三个线程:main()主线程、gc()垃圾收集线程和异常处理线程。当然,如果发生异常,就会影响到主线程。多线程程序的优点:提高应用程序的响应能力。对图形界面更有意义并增强用户体验。一次做多件事。例如:一边写代码一边听音乐。提高计算机系统CPU利用率。但是,线程也会带来额外的开销,比如CPU调度时间和并发控制带来的系统开销。改进程序结构。将一个冗长复杂的进程分成多个线程独立运行,便于理解和修改。什么时候需要多线程?一个程序需要同时执行两个或多个任务。当需要一些后台运行的程序时,如:Java后台运行的GC线程。Java中创建线程的方式有四种,下面我们一一介绍。1.继承Thread类(1)定义Thread类的子类,重写该类的run方法。run方法的方法体表示线程要完成的任务。因此,run()方法被称为执行体。(2)创建Thread子类的实例对象,即创建线程对象。(3)调用线程对象的start()方法启动线程。示例代码:publicclassMyThreadextendsThread{//总票数publicintcount=10;@Overridepublicvoidrun(){//还有票时继续售票while(count>0){//剩余票数count--;System.out.println(Thread.currentThread().getName()+"销售数量"+(10张)+"票数,当前剩余票数:"+count);}}publicstaticvoidmain(String[]args){MyThreadmyThread=newMyThread();myThread.start();}}2.实现Runnable接口(1)定义Runnable接口的实现类,重写接口的run()方法。run()方法的方法体也是线程的线程执行体。(2)创建Runnable实现类的实例对象,并以这个实例为Thread的目标创建Thread对象,Thread对象才是真正的线程运行对象。(3)调用线程对象的start()方法启动线程。示例代码:publicclassMyRunableThreadimplementsRunnable{//总票数publicintcount=10;@Overridepublicvoidrun(){//还有票时继续卖出while(count>0){//剩余票数count--;System.out.println(Thread.currentThread().getName()+"销售数量"+(10张)+"票数,当前剩余票数:"+count);}}publicstaticvoidmain(String[]args){MyRunableThreadmyRunableThread=newMyRunableThread();ThreadmyThread=newThread(myRunableThread);myThread.start();}}《Thread和Runnable的区别》以上两种方法是最常见的两种创建线程的方法,也是经常被问到的两种方式创建线程区别简述如下:“继承Thread类”子类继承具有多线程能力的Thread类启动线程:子类线程对象调用.start()方法。不推荐使用:避免OOP单继承的局限性“实现接口Runnable”多线程能力启动线程:传入目标对象+Thread对象调用。start()方法推荐:避免单继承的限制,方便同一个对象被多个线程使用另外,使用线程池时,只能实现Runable或Callable线程,不能直接放到继承Thread3的类中.实现Callable接口(1)创建一个Callable接口的实现类,实现call()方法。call()方法会作为线程执行体,有返回值。(2)创建Callable实现类的实例,使用FutureTask类封装Callable对象,FutureTask对象封装了Callable对象的call()方法的返回值。(3)使用FutureTask对象作为Thread对象的目标来创建并启动一个新的线程。(4)调用FutureTask对象的get()方法获取子线程执行完成后的返回值。示例代码:publicclassMyCallableThreadimplementsCallable{//总票数privateintcount=10;@OverridepublicStringcall()throwsException{//还有票时继续卖出while(count>0){//剩余票数统计--;System.out.println(Thread.currentThread().getName()+"sale"+(10-count)+"门票,当前剩余门票数量:"+count);}return"门票已售完";}publicstaticvoidmain(String[]args)throwsInterruptedException,ExecutionException{Callablecallable=newMyCallableThread();FutureTaskfutureTask=newFutureTask<>(callable);ThreadmyThread=newThread(futureTask);myThread.start();//PrintreturnresultSystem.out.println(futureTask.get());}}《Runnable和Callable的区别:》Callable指定的方法是call(),Runnable指定的方法是run()。Callable任务执行后可以返回值,而Runnable任务不能返回值。call方法可以抛出异常,run方法不能。4、线程池Java默认提供了五个线程池,由Executors创建。创建一个新线程。“newFixedThreadPool”创建一个固定长度的线程池,可以控制最大并发线程数,超出的线程会在队列中等待。“newScheduledThreadPool”创建一个支持定时和周期性任务执行的定长线程池。“newSingleThreadExecutor”创建一个单线程线程池,它只会使用唯一的工作线程来执行任务,确保所有任务都按照指定的顺序(先进先出,后进先出,优先级)执行。“newWorkStealingPool”创建一个具有抢占式操作的线程池。因为它可以合理的使用CPU来进行任务运算(并行运算),所以适合做耗时的任务。示例代码:publicclassMyThreadPoolimplementsRunnable{//总票数publicintcount=10;@Overridepublicvoidrun(){//还有票时继续卖出while(count>0){//剩余票数count--;System.out.println(Thread.currentThread().getName()+"销售数量"+(10-count)+"门票,当前剩余门票数量:"+count);}}publicstaticvoidmain(String[]args){ExecutorServiceex=Executors.newFixedThreadPool(5);MyThreadPoolt=newMyThreadPool();ex.submit(t);ex.shutdown();}}关于线程池,后面会有单独的文章详细介绍。通过以上内容,希望大家对线程有一个初步的了解,相关的示例代码,我稍后整理后会上传到github,敬请关注我们的后续文章。