线程
线程的六个状态
-
新创建
当用new操作符创建一个新线程时,该线程还没有开始运行。
-
可运行
调用start方法,线程就处于runnable状态。 在任何时刻,一个线程可能处于运行也可能没有运行。
当线程处于阻塞或者等待状态,线程暂时不活动,不运行任何代码且消耗最少的资源,直到线程调度器重新激活。
-
被阻塞
当线程试图获取一个内部的对象锁,而这个锁由其他线程持有,则该线程进入阻塞状态。
-
等待
当线程等待另一个线程通知调度器一个条件时,就会进入等待状态。
-
计时等待
例如Thread.sleep方法就会让线程进入计时等待,这个状态会一直持续到超时期满或者接收到适当的通知。
-
被终止
run方法正常退出而死亡 因为一个没有捕获的异常导致run终止也会死亡。
关系
当多个线程对同一个对象同一时间进行读取,那么就需要对这个进行加锁操作,防止出现两个线程同时修改一个对象(共享数据)。
给对象加锁之后,线程访问同一个对象时,锁会以串行的方式提供服务,即线程A请求到对象,加锁,线程B请求对象,因为对象被加上了锁,线程B等待线程A释放锁,线程A释放锁,线程B请求,加锁。。。
对于加锁尽量在finally语句中释放锁,如果在临界区的代码抛出异常,锁必须得到释放,不然其他线程会永远阻塞,所以最好在finally语句中释放锁。
锁与条件:
1. 锁用来保护代码片段,任何时刻只能有一个线程执行被保护的对象。
2. 锁可以管理试图进入被保护代码段的线程
3. 锁可以拥有一个或者多个对象
4. 每个条件对象管理哪些已经进入被保护的代码段但不能运行的线程。
可见性:当一个线程修改一个共享数据时,另外一个线程可以读到这个修改的值。
同步
1. 对于同步方法,锁是当前实例对象
2. 对于同步静态方法,锁是当前对象的Class对象
3. 对于同步方法块,锁是synchronized括号里面配置的对象。
Volatile
- Volatile是一个轻量级的synchronized,它在多处理器开发中保证了共享数据的可见性。
- 如果一个字段被声明为volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。
- 不会引起线程上下文的切换和调度。
Volatile与synchronized
- synchronized 解决的是线程问题中的执行控制,也就是控制代码执行顺序及是否可以并发执行。volatile则是解决的内存可见,也就是线程执行结果在内存中对与其他线程是可见的。
- volatile并不具备原子性,例如i++操作,volatile只能保证线程操作的i是同一块内存的,但可能会出现写入脏数据的情况,因为i++是三步,读i、++、写i。但是volatile不光具有修改可见性,还具备了原子性。
- volatile只能用于变量,synchronized则可以用于变量,方法还有类级别的东西。
- volatile不会造成线程阻塞,synchronized可能会造成线程阻塞。
线程问题的处理:
可以通过一个或者多个队列将其形式化,生产者线程向队列添加元素,消费者则消费他们,使用队列,可以安全的从一个线程向另一个线程传递数据。
当试图向队列添加元素但队列满了,或者是想移除元素但队列空了,那么就阻塞队列。
线程池
创建一个新的线程,需要与操作系统交互,是有代价的,对于那些生命周期短的线程,但是数量很多,我们可以去使用线程池。
一个线程池中有需要准备运行的空闲线程。
将Runnable对象交给线程池,就会有一个线程调用run方法。
run方法退出时,线程不会死亡,而是回到池中准备为下一个请求提供服务。