我们先看一个例子
public class SynchronizedDemo {
static Integer count=0;
public static void incr(){
count++;
}
public static void main(String[] args) throws IOException, InterruptedException {
for(int i=0;i<1000;i++){
new Thread(()->SynchronizedDemo.incr()).start();
}
Thread.sleep(2000);
System.out.println("result:"+count);
}
}
复制代码
最后的输出我们可以看一下
一定是一个小于等于1000的值,这是一个比较经典的代码场景了,小于1000的原因也简单就是可见性和原子性的原因
可见性:线程看不到变量的最新值,因为它还没有被另一个线程写回主内存,这个问题被称为“可见性”问题。一个线程的更新对其他线程是不可见的。
原子性: 原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。原子性就是指该操作是不可再分的。 count++
看着像是一句代码好像也不能够再怎么拆分步骤,但其实底层是分为好几步的,我们可以使用 javap -v xx.class
指令查看对应字节码如下: 发现先是访问这个值然后声明一个常量压入到栈中,然后再加加操作最后就是赋值给静态变量(大概的流程其实就是需要在内存中声明一个变量把原来变量的值赋值给新变量对这个新值进行++最后赋值给初始变量),所以这里就会出现原子性问题如下图所示:
Synchronized的使用
针对上面的问题呢解决方法其实也比较简单就是加锁,加锁之后就能实现多个线程不能同时执行同一个任务修改后的代码如下:
public class SynchronizedDemo {
static Integer count=0;
public static void incr(){
synchronized (SynchronizedDemo.class) {
count++;
}
}
public static void main(String[] args) throws IOException, InterruptedException {
for(int i=0;i<1000;i++){
new Thread(()->SynchronizedDemo.incr()).start();
}
Thread.sleep(2000);
System.out.println("result:"+count);
}
}
复制代码
可以发现打印结果一定是1000
当然Synchronized
的使用方法不止这一种还有如下三种方式:
- 使用对象示例的锁
public class SynchronizedDemo { static Integer count=0; static Object obj = new Object(); public static void incr(){ synchronized (obj) { count++; } } public static void main(String[] args) throws IOException, InterruptedException { for(int i=0;i<1000;i++){ new Thread(()->SynchronizedDemo.incr()).start(); } Thread.sleep(2000); System.out.println("result:"+count); } } 复制代码
- Synchronized修饰在类方法上
public class SynchronizedDemo { static Integer count=0; public synchronized static void incr(){ count++; } public static void main(String[] args) throws IOException, InterruptedException { for(int i=0;i<1000;i++){ new Thread(()->SynchronizedDemo.incr()).start(); } Thread.sleep(2000); System.out.println("result:"+count); } } 复制代码
- 修改在实例方法上
public class SynchronizedDemo { static Integer count=0; public static synchronized void incr(){