高并发之Synchronized
原文链接:高并发之Synchronized
Synchronized简介
Synchronized的作用
官方解释
Synchronized methods enable a simple strategy for preventing thread interference and memory consistency errors:if an object is visible to more than one thread,all reads or writes to that object’s variables are done through synchronized methods.
一句话说出Synchronized的作用
能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
Synchronized的地位
- Synchronized是Java的一个关键字
- 是最基本的互斥同步的手段
- 是并发编程中的元老级角色,是并发编程的必学内容
不用并发手段的后果
代码实战:两个线程同事a++,最后结果会比预期少
/**
* @program: concurrency_demo
* @Author: RONGHUA.YU@HAND-CHINA.COM
* @Date: 2019/9/17 15:14
* @Description: 高并发之消失的请求,两个线程对i++,执行100000次,结果一定小于200000
*/
public class DisappearRequest1 implements Runnable{
static DisappearRequest1 disappearRequest1 = new DisappearRequest1();
static int i = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(disappearRequest1);
Thread t2 = new Thread(disappearRequest1);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
@Override
public void run() {
for (int j = 0; j < 100000; j++) {
i++;
}
}
}
运行结果:
132889
原因:
count++,它看上去只是一个操作,实际上包含了三个动作:
- 读取count
- 将count加一
- 将count的值写道内存中
这三个动作执行到任何一个动作时,都有可能被打断,如此时count为9,线程a执行到+1操作,count的值变为10.但是还没来得及写入内存,线程b就开始执行,此时读取到的count依然是9,这和我们的预期就不同了,我们称之为线程不安全
Synchronized的两种用法
Synchronized的两种用法介绍
对象锁,包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)
类锁,指synchronized修饰静态的方法或指定锁为Class对象
第一个用法:对象锁
代码块锁
代码块形式:手动指定锁对象
锁对象的选取:若不特定锁对象,可以用this(当前对象)作为我们的锁。
但有时候情况比较复杂,需要我们自己去选取锁对象,比如我们有多个个synchronized代码块,不是它们其中一个执行其它的就不能执行,而是可以同步执行,如下例,定义了两个Object类型的对象作为我们的锁,此时代码运行就可以是两两配对地运行。
/**
* @program: concurrency_demo
* @Author: RONGHUA.YU@HAND-CHINA.COM
* @Date: 2019/9/12 14:08
* @Description: 对象锁示例一:代码块锁
*/
public class SynchronizedObjectCodeBlock2 implements Runnable {
static SynchronizedObjectCodeBlock2 instance = new SynchronizedObjectCodeBlock2();
final Object lock1 = new Object();
final Object lock2 = new Object();
@Override
public void run() {
//代码块形式
synchronized (lock1) {
System.out.println("我是lock1。我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "lock1运行结束");
}
synchronized (lock2) {
System.out.println("我是lock2。我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "lock2运行结束");
}
}
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while(t1.isAlive()||t2.isAlive()){
}
System.out.println("finished");
}
}
运行结果:
我是lock1。我叫Thread-0
Thread-0lock1运行结束
我是lock2。我叫Thread-0
我是lock1。我叫Thread-1
Thread-1lock1运行结束
Thread-0lock2运行结束
我是lock2。我叫Thread-1
Thread-1lock2运行结束
finished
除了这种情况,可能还有更复杂的情形,比如三个线程等待一个线程,再比如线程之间还有通信,就很难处理了,可能通过我们自己的努力也是能写出来,但是不建议自己写,因为如果稍有疏忽,就可能出现比较大的错误,那怎么去避免?实际上jdk为我们提交了几个非常完善的同步控制工具类,例如CountDownLatch、CyclicBarrier、Semaphore、Exchanger等等,有兴趣的可以去了解。
方法锁形式
synchronized修饰普通方法(非静态方法),锁对象默认为this
/**
* @program: concurrency_demo
* @Author: RONGHUA.YU@HAND-CHINA.COM
* @Date: 2019/9/12 14:08
* @Description: 对象锁示例二:方法锁
*/
public class SynchronizedObjectMethod3 implements Runnable {
static SynchronizedObjectMethod3 instance = new SynchronizedObjectMethod3();
@Override
public void run() {
method();
}
private synchronized void method() {
System.out.println("我是对象锁的方法修饰符形式,我叫" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行结束");
}
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive