笔记-Java线程概述

Java Concurrency in Practice中对线程安全的定义:当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替运行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么这个类就是线程安全的。

显然只有资源竞争时才会导致线程不安全,因此无状态对象永远是线程安全的 。

过多的同步会产生死锁的问题,死锁属于程序运行的时候发生的一种特殊的状态。

原子操作: 多个线程执行一个操作时,其中任何一个线程要么完全执行完此操作,要么没有执行此操作的任何步骤 ,那么这个操作就是原子的。
原子操作:根据Java规范,对于基本类型的赋值或者返回值操作,是原子操作。但这里的基本数据类型不包括long和double,因为JVM看到的基本存储单位是32位,而long和double都要用64位来表示。所以无法在一个时钟周期内完成。
(自增操作[++]不是原子操作,因为它涉及到一次读和一次写。)

线程的生命周期
新建(new Thread)线程进入新建状态(未被启动)
就绪(runnable)此时线程正在就绪队列中排队等候得到CPU资源。
运行(running)此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
堵塞(blocked)一个睡眠着的线程在指定的时间过去可进入就绪状态。正在等待:调用wait()方法。(调用motify()方法回到就绪状态)
死亡(dead)这时线程不可能再进入就绪状态等待执行。

避免死锁通用的经验法则:
当几个线程都要访问共享资源A、B、C时,保证每个线程都按照同样的顺序去访问他们。

多线程的优点
线程中的处理程序依然是顺序执行,符合普通人的思维习惯,所以编程简单。
多线程的缺点
线程的使用(滥用)会给系统带来上下文切换的额外负担(电脑程序开很多配置如果不好,电脑会很卡)。并且线程间的共享变量可能造成死锁的出现。(死锁以上说过)

volatile相当于synchronized的弱实现,也就是说volatile实现了类似synchronized的语义,却又没有锁机制。它确保对volatile字段的更新以可预见的方式告知其他的线程。

volatile语义:
(1)Java 存储模型不会对valatile指令的操作进行重排序:这个保证对volatile变量的操作时按照指令的出现顺序执行的。
(2)volatile变量不会被缓存在寄存器中(只有拥有线程可见)或者其他对CPU不可见的地方,每次总是从主存中读取volatile变量的结果。也就是说对于volatile变量的修改,其它线程总是可见的,并且不是使用自己线程栈内部的变量。也就是在happens-before法则中,对一个 valatile变量的写操作后,其后的任何读操作理解可见此写操作的结果。
尽管volatile变量的特性不错,但是volatile并不能保证线程安全的,也就是说volatile字段的操作不是原子性的,volatile变量只能保证可见性(一个线程修改后其它线程能够理解看到此变化后的结果),要想保证原子性,目前为止只能加锁!

应用volatile变量的三个原则:
(1)写入变量不依赖此变量的值,或者只有一个线程修改此变量
(2)变量的状态不需要与其它变量共同参与不变约束
(3)访问变量不需要加锁

独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。而另一个更加有效的锁就是乐观锁。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

乐观锁用到的机制就是CAS,Compare and Swap。

Java通过Executors提供了四种线程池
1、Executors. newCachedThreadPool()创建一个 可缓存的线程池,如果线程池长度超过处理需要, 可灵活回收空闲线程,如无回收,则新建线程
2、Executors. newFixedThreadPool(num)创建一个 定长线程池可控制线程最大并发数,超出的线程会在队列中等待
3、Executors. newScheduledThreadPool(num)创建一个 定长线程池支持定时及周期性任务执行
4、Executors. newSingleThreadExecutor()创建一个 单线程化的线程池,它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序执行


你可能感兴趣的:(Java)