一 . 概述
在前面我们说到线程安全性问题解决的核心就是同步,同步的核心就是保证原子性.
在java之中最早就支持语法层面的同步解决了,并且提供了synchronized的方式解决问题.
二 .内置锁
在java之中每一个对象都是一个内置锁,这个在JVM的体系之中就规定好了.
内置锁的规定也就决定我们可以拿任意的对象进行同步操作.
内置锁常常配合synchronized使用.
三 .synchronized
该关键词的作用是同步,需要配合内置锁进行使用.
常见的synchronized的使用方式有三种:
[1]在静态方法中使用
[2]在实例方法中使用
[3]在同步代码块之中使用
其中: 在静态方法之中的内置锁是该类的class对象,实例方法的内置锁是调用该方法的实例对象.
在同步代码块之中的内置锁需要显示的指定.
四 . 例子
将我们之前的计数代码进行改写,变成线程安全的操作.
public class ProblemSolve { private int count = 0; public static void main(String[] args) throws Exception { ProblemSolve demo = new ProblemSolve(); Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int x =0;x<10000;x++) { demo.add(); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { for(int x =0;x<10000;x++) { demo.add(); } } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("获取最终的结果count : " + demo.count); } private synchronized void add() { count++; }}
我们所作出的概念仅仅是加上了一个synchronized关键词就完成了同步的操作,这样我们就能保证线程安全性.
五 .分析
当我们的代码进入同步代码的时候,会试图获取内置锁.
获取成功: 继续运行
获取失败: 进入该内置所的阻塞队列.直到获取该所的线程完成任务或者自己释放内置所的拥有权.
然后阻塞队列的线程重新进入runnable状态,再此尝试获取该锁.
以上就是synchronized同步的底层运行方式.
六. synchronized的优劣
优势 : 保证原子性,因为同步的代码仅仅只有一个线程可以运行.
劣势: 粒度过大,获取不到内置锁的线程会进入阻塞队列等待唤醒,这个是一刀切的方式,很有可能会影响性能.
解决: 在jak6之后,对synchronized关键词的实现进行了优化,这个在下面会介绍.