线程创建形式
- 继承ThreadLocal
- 实现Runnable
- 实现Callable:有返回值的任务,用Future接受返回值
- 使用线程池ExecutorService实现
- newCachedThreadPool 不定长线程池
- newFixedThreadPool 定长线程池
- newScheduledThreadPool 定时线程池
- newSingleThreadExecutor 串行线程池
sleep() 和 wait()
Synchronized使用方法
ThreadLocal
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。JDK中提供的ThreadLocal类正是为了解决这样的问题。 ThreadLocal类主要解决的就是让每个线程绑定自己的值,可以将ThreadLocal类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。
ThreadLocal 内存泄露问题:
ThreadLocal,中使用的 key 为 ThreadLocal 的弱引用,而 value 是强引用。所以,如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会key
会被清理掉,而value
不会被清理掉。
在调用set()、get()、remove() 方法的时候,会清理掉key
为null
的记录。
乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。
版本号机制
每次修改一个数据,一般是在数据表中加上一个数据版本号version字段。操作示意如下:
- 数据库里有金额100,version=1
- A读取金额100,修改金额为50,修改version=2
- B读取金额为100,修改金额为60,修改version=2
- A提交到库,这时有一个判断,如果当前version>库里的version,那么通过,A可以通过
- 由于A修改了库version=2,B的version没有大于2,被驳回,不可提交
CAS
CAS 适合简单对象的操作,比如布尔值、整型值等。
内部保持着一个volatile修饰的long变量,volatile保证了long的值更新后,其他线程能立即获得最新的值。
自旋锁,比较交换算法,需要读写的内存值V,进行比较的值A,拟写入的值B
- 如果A取到的Value为1,要对value进行加1操作,设置期望值为2
- 如果B取到的Value为1,要对value进行+1,设置期望值为2
- A判断value+1为2,与期望值相等,那么修改Value为2
- B判断实际的V+B=3,与期望的2不相等,那么返回失败,继续第二步,这时读出的Value为2,循环一次。
悲观锁
传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
Java中synchronized
和ReentrantLock
等独占锁就是悲观锁思想的实现。