Synchronized 精讲

并发场景中,保证同一时刻只有一个线程对有并发隐患的代码进行操作

1.2 错误案例

需求:两个线程对 count 变量进行200000次循环增加,预期结果是400000次

public class SynchronizedDemo implements Runnable { private static int count = 0; static SynchronizedDemo synchronizedInstance = new SynchronizedDemo(); public static void main(String[] args) { Thread t1 = new Thread(synchronizedInstance); Thread t2 = new Thread(synchronizedInstance); t1.start(); t2.start(); try { t1.join(); t2.join(); System.out.println("count 最终的值为: " + count); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void run() { synchronized (this) { for (int i = 0; i < 200000; i++) { count++; } } } }

结果 :显然不等于400000次所以出现了运算错误

图片

原因:

count++;

该语句包含三个操作:

线程t1、t2 从主内存中获取共享变量count的值,到自己的工作内存中

将自己的工作内存中的count值进行+1操作

将修改完的count变量的值存入到主内存中

图片

注意:他们是将自己工作内存中的值进行改变刷回主内存,假设当前count的值为8,t1、t2将count的值复制到自己的工作内存中进行修改,如果此时t1将count变成9、t2此时也将count的值变成9,当t1、t2两个线程都将值刷回主内存的时候count值为9,并不是10,这个时候就会造成最后的结果和预期的不一致。

1.3 正确案例

代码块上加对象锁 this

@Override public void run() { synchronized (this) { for (int i = 0; i < 200000; i++) { count++; } } }

在普通方法上加锁

@Override public synchronized void run() { for (int i = 0; i < 200000; i++) { count++; } }

加.class锁

@Override public void run() { for (int i = 0; i < 200000; i++) { synchronized (SynchronizedDemo.class) { count++; } } }

输出结果:

在这里插入图片描述

后文详细讲解四种加 synchronized 的方式

2.用法 2.1 对象锁

2.1.1 方法锁

修饰普通方法默认锁对象为this当前实例对象

public synchronized void method() ;在普通方法上面加synchronized

public class SynchronizedDemo3 implements Runnable { static SynchronizedDemo3 synchronizedDemo3 = new SynchronizedDemo3(); public synchronized void method() { System.out.println("线程名称" + Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程名称" + Thread.currentThread().getName() + "运行完成"); } @Override public void run() { method(); } public static void main(String[] args) { Thread t1 = new Thread(synchronizedDemo3); t1.setName("我是线程 t1"); Thread t2 = new Thread(synchronizedDemo3); t2.setName("我是线程 t2"); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }

输出结果: 线程 t1 和线程 t2 执行过程是顺序执行的

图片

2.1.2 同步代码块

代码示例:没有加锁而定义的两个线程执行的情况

在这里插入图片描述

输出结果:线程 t1 和线程 t2 交叉执行形成了乱序

图片

代码示例:加Synchronized 锁而定义的两个线程执行的情况,锁对象的是this(当前对象)

图片

输出结果:线程 t1 和线程 t2 执行过程是顺序执行的

图片

代码示例:加Synchronized 锁而定义的两个线程执行的情况,锁对象的是自定义对象

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjdfy.html