今天,课课家就跟大家探讨一下java多线程入门,这方面的知识其实很简单的,只要你认真的阅读文章,我保证你一定可以学会JAVA多线程入门。有需要的小伙伴,可以参考一下。
为什么使用多线程
进程之间不能共享内存,但线程之间共享内存很容易
系统创建进程需要为该进程重新分配系统资源,但创建线程代价小得多,因此使用多线程来实现多任务并发比多进程的效率高
JAVA内置了多线程功能支持,而不是单纯的作为底层操作系统的调度方式,从而简化了JAVA的多线程编程
线程的创建
继承Thread类(可直接使用this关键字获得当前对象,多个线程无法共享线程类的实例变量)
实现Runnable接口(必须使用Thread.currentThread()方法,多个线程可以共享线程类的实例变量)
使用Callable和Future创建线程(call()方法作为线程执行体,可以有返回值去,可以声明抛出异常)
线程的生命周期
新建(New)--使用new关键词新建一个线程
就绪(Runnable)--调用start()方法
运行(Running)--处于就绪状态的线程获得CPU,开始执行run()方法体
阻塞(Blocked)
线程调用sleep()方法主动放弃所占用的处理器资源
线程调用了一个阻塞式的IO方法
线程试图获得一个同步监视器,但该监视器被其他线程所持有
线程在等待某个通知
程序调用了线程的suspend()方法将该线程挂起(容易导致死锁)
死亡(Dead)
run()方法或call()方法执行完成,线程正常结束
线程抛出一个未捕获的异常
直接调用该线程的stop()方法来结束该线程(容易导致死锁)
控制线程
join()方法--当某个程序执行流中调用其他程序的join()方法时,调用线程被阻塞,直到join()方法加入的join线程执行完为止
setDemon()方法--将指定线程设置为守护线程
sleep()方法--暂停线程的执行,并进入阻塞状态
yield()方法--暂停线程的执行,并进入就绪状态
setPriority()方法--改变线程的优先级,让优先级高的线程获得更多的执行机会
Java线程具有五中基本状态
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Threadt=newMyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞--线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞--通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
如果对什么是线程、什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内。
用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。
很多人都对其中的一些概念不够明确,如同步、并发等等,让我们先建立一个数据字典,以免产生误会。
多线程:指的是这个程序(一个进程)运行时产生了不止一个线程
并行与并发:
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
线程同步
同步代码块(同步监视器为obj)
synchronized(obj){...}
同步方法(同步监视器为this)
publicsynchronizedvoiddraw{}
同步锁(Lock)
//定义锁对象
privatefinalReentrantLocklock=newReentrantLock();
//加锁
lock.lock();
//释放锁
lock.unlock();
释放同步监视器的锁定
当前线程的同步方法,同步代码块执行结束
当前线程在同步方法,同步代码块遇到break,return终止了继续执行
当前线程在同步方法,同步代码块出现未处理的异常
当前线程执行同步方法,同步代码块时执行了同步监视器对象的wait()方法
不释放同步监视器的情况
线程执行同步方法,同步代码块时,程序调用Thread.sleep(),Thread.yield()方法来暂停当前线程的执行
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起
线程通信
传统的线程通信
对于使用synchronized修饰的同步方法或代码块,借助Object类提供的wait(),notify(),notifyAll()方法
使用Condition控制线程通信
如果程序显式使用Lock对象保证同步,则使用Condition对象的await(),signal(),signalAll()来控制程序的协调运行
使用阻塞队列(BlockingQueue)控制线程通信
BlockingQueue接口:put()方法尝试把元素放入队列中,入托队列元素已满,则阻塞该线程,take()方法尝试从队列头部去取出元素,如果队列元素已空,则阻塞该线程
线程池
Executors工厂类来产生线程池
//返回ExecutorService对象的方法
newCachedThreadPool()
newFixedThreadPool(intnThreads)
newSingleThreadExecutor()
//返回ScheduleExecutorService线程池的方法
newScheduledThreadPool(intcorePoolSize)
newSingleThreadScheduledExecutor()
//生成workstealing池,相当于后台线程池
ExecutorServicenewWorkStealingPool(intparallelism)
ExecutorServicenewWorkStealingPool()
创建Runnable或者Callable实现类的实例
调用ExecutorService对象的submit()方法来提交Runnable或者Callable实例
调用ExecutorService对象的shutdown()方法关闭线程池
ForkJoinPool--充分利用多核CPU
创建ForkJoinPool实例
创建有继承了返回值的RecursiveTask或无返回值的RecursiveAction实例
调用ForkJoinPool的submit(ForkJoinTasktask)或submit(ForkJoinActionaction)方法来执行指定任务
线程相关类
ThreadLocal类
隔离多个线程的数据共享,从根本上避免多个线程之间对共享资源的竞争
包装线程不安全的集合类
使用Collections提供的类方法包装
线程安全的集合类
以Concurrent开头的集合类
以CopyOnWrite开头的集合类
结束语:以上就是本篇文章的内容,看完文章的小伙伴都学会JAVA多线程入门了吧? 如果想更深的了解JAVA多线程,随时可以登陆课课家教育平台浏览,这里的知识内容全面,相信你一等会有不少的收获的。
¥29.90
¥798.00
¥199.00
¥48.00¥180.00
¥199.00
¥48.00¥180.00