最近做HBase项目有一块涉及到Java的多线程安全的问题,于是回顾了一下java多线程的东西,看了多篇多线程的写的比较好的文章之后,对这一块的知识做一个回顾总结,并附上参考文章。
第一篇会写正题,ThreadLocal的原理和用法。
目录
一、程序 & 进程 & 线程
程序(program):为了完成特定任务,我们编写的一段代码,就是一段程序。
进程(process):是程序的一次执行过程,或者是正在运行中的一个程序。
线程(thread):一个程序内部的一个执行路径,一个进程可以包含多条线程。
通俗的理解就是:
同一时间只运行一次某一段程序,就叫进程;
同一时间多次运行一段相同的程序,就叫这段程序的进程中有多个线程。
这样多线程的优点是,同一时间可以对同一段程序,多次运行,得出不同的结果。
ps:进程中的内存,对于多线程是共享的
ps:附上一篇对这三个概念讲解的很好的文章:图文理解CPU、进程、线程
二、多线程的使用场景
1. 程序需要同时执行多个任务;
2. 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等;
3. 需要一些后台运行的程序时。
ps:每一个java程序都有一个隐含的主线程:main方法
三、 多线程的优点和缺点
1. 优点
(1)提高CPU的使用率
例如上传图片到网上,这个操作是一个进程;
单线程情况下,9张图片我们上传9次,每一次这个线程只占用CPU1%的内存;
多线程情况下,开启9条线程同时上传9张图片,上传过程中9条线程占用CPU9%的内存。
(2)提高效率
还是上面上传图片到网上的例子,单线程情况下,1张图片上传需要1秒,那9张图片就需要上传9秒;
多线程只需要1秒就能把9张图片全部上传。
(3)改善程序结构
将既长又复杂的进程,拆分成多个线程,独立运行,便于修改和理解。
2. 缺点
(1)大量线程会影响性能,因为CPU需要在它们之间切换;
(2)多线程需要更多的内存空间
(3)当多个线程同时访问一个变量对象时,可能出现线程安全问题,例如一个线程刚访问完,对象数据还没更改,下一个线程就接着访问,这样获得的数据是错误的,这就是线程安全问题
(4)多线程同时访问一个对象,可能会出现死锁问题
四、 多线程并行 & 并发
(1)并行
多个处理器或者多核处理器,同时执行多个不同的任务;例如8个核心同时运行8个线程,就是典型的并行,提高CPU计算效率。
(2)并发
一个处理器,处理多个任务,多个任务通过抢占时间片的方式,同一时间只有一个线程在执行,运行完了下一个线程抢到时间片继续运行,但由于CPU的运行速度很快,所以基本上可以看做是多个线程同时运行。
ps:并行中包含并发,多核CPU并行运行,每一个核心又同时运行多个并发线程。
五、线程的两种常见创建方式
1. Thread类的三种构造方法
(1)Thread():创建新的线程对象
(2)Thread(Runnable target):指定创建线程的目标方法,即实现了Runnable接口中的run方法
(3)Thread(Runnable target,String name):创建新的线程对象,指定创建线程的目标方法,和当前线程名称
2. 创建线程的两种常用方式
(1)实现Runnable接口,在实现类中重写Runnable接口中的run方法
(2)继承Thread类,在继承类中重写Thread类中的run方法
对比一下,实现Runnable接口的好处在于避免了单继承的局限性,并且适合多个线程共享一个接口实现类的对象,非常适合多个相同线程处理同一个共享资源。
六、Thread类的11个相关方法
(1)void start():开启线程,并执行对象的run()方法
(2)run():线程的实现方法
(3)String getName():返回线程名称
(4)void setName(String name):设置线程名称
(5)static curentThread():返回当前线程
(6)static void yield():线程让步,暂停正在执行的当前线程,让步给优先级更高的线程先执行
(7)join():调用其他线程,join进来的线程优先执行完
(8)static void sleep(long millis):线程睡眠,指定时间是毫秒级
(9)stop():强制线程结束
(10)boolean isAlive():判断线程是否还活着
(11)setDaemon(true):把当前线程变成守护线程
七、线程池
1. 为什么需要线程池
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程,并且大量线程空闲,就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。
这个时候,线程池的作用就出来了,
当程序需要创建大量生命周期很短暂的线程时,使用线程池可以有效控制系统中并发线程的数量,
线程池中,一个线程于空闲状态时,可以被使用,使用时变为阻塞状态,使用后又变成空闲状态,这样就可以实现线程的重复利用,这也是线程池的设计初衷。
2. 线程池的创建
在Java 5之后,可以使用工厂类Executors来创建线程池
(1)newFixedThreadPool:创建固定大小的线程池
(2)newSingleThreadExecutor:创建一个单线程的线程池
(3)newCachedThreadPool:创建带有缓存的线程池
(4)newScheduledThreadPool:创建定时和周期性执行的线程池
八、相关参考文章: