💡💡synchronized关键字是Java语言为开发人员提供的同步工具,可以将它看成是一个“语法糖”
synchronized要解决的问题就是——多线程并发执行过程中数据同步的问题
Java通过synchronized指定同步块,从而能在指定块中避免数据竞争问题,对方法进行声明实际上也有一个对应的同步块范围,而且会指定一个对应的锁对象。同一时刻只有一个线程能进入锁中,其他线程必须等待锁里的线程出来后才能够依次进入。
synchronized不同的作用方式会导致不一样的作用范围,可通过修饰不同的对象实现锁范围,在代码上则是体现为代码块
这里会扯到操作的原子性,不了解的朋友可以先去了解一下什么是原子性操作
总的来说,它们之间的区别就是:
作用在对象方法上和作用在对象方法里面,它们锁住的对象是一样的,作用范围都是在对象上,对象充当了锁。(注意:类可以实例化多个对象,这个时候每个对象都是一个锁,即每个对象锁的作用范围都是相对于自己的对象来说的。)
剩下的两个作用在类静态方法上和方法里面的,它们锁住的是类本身
下面来几个简单案例
最终想要实现的效果是两个都输出60000
package com.java.code6;
public class SynchronizedDemo2 {
int count = 0;
int count2 = 0;
public void add() {
count++;
}
public synchronized void add2() {
count2++;
}
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo2 demo = new SynchronizedDemo2();
for (int i = 0; i < 10; i++){
new Thread(() -> {
for (int j = 0; j < 6000; j++){
demo.add();
}
}).start();
}
for (int i = 0; i < 10; i++){
new Thread(() -> {
for (int j = 0; j < 6000; j++){
demo.add2();
}
}).start();
}
Thread.sleep(3000);
System.out.println("count = " + demo.count);
System.out.println("count2 = " + demo.count2);
}
}
可以看到只有count2完成了我们的预期:60000
这里可以不用关心count的最终结果,因为它肯定小于60000,因为在count对应的方法里这里的++操作是非原子的操作
问:如果是两个不同的方法呢?如果是两个不同的方法,那么效果会是怎么样的呢?
以下定义了两个方法,看看效果
package com.java.code6;
public class SynchronizedDemo8 {
public synchronized void method(String name) {
System.out.println(name + " gets the lock.");
sleep(3000);
System.out.println(name + " releases the lock after 3s.");
}
public synchronized void method2(String name) {
System.out.println(name + " gets the lock.");
sleep(3000);
System.out.println(name + " releases the lock after 3s.");
}
public static void sleep(int s) {
try {
Thread.sleep(s);
} catch (InterruptedException e) {
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo8 demo = new SynchronizedDemo8();
new Thread(() -> {
demo.method("thread1");
}).start();
new Thread(() -> {
demo.method2("thread2");
}).start();
}
}
同样这两个线程还是会产生互斥效果,声明的两个不同对象方法都是以当前对象作为锁,那么肯定就还是会产生互斥效果。
作用在类静态方法上是以类作为锁🍐🍐🍐
package com.java.code6;
public class SynchronizedDemo4 {
static int count = 0;
public synchronized static void add() {
count++;
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++){
new Thread(() -> {
for (int j = 0; j < 10000; j++){
add();
}
}).start();
}
Thread.sleep(3000);
System.out.println("count = " + count);
}
}
可以看到,达到我们的目标:100000
package com.java.code6;
public class SynchronizedDemo5 {
public synchronized void method(String name) {
System.out.println(name + " gets the lock.");
sleep(3000);
System.out.println(name + " releases the lock after 3s.");
}
public static synchronized void method2(String name) {
System.out.println(name + " gets the lock.");
sleep(3000);
System.out.println(name + " releases the lock after 3s.");
}
public static void sleep(int s) {
try {
Thread.sleep(s);
} catch (InterruptedException e) {
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedDemo5 demo = new SynchronizedDemo5();
new Thread(() -> {
demo.method("thread1");
}).start();
new Thread(() -> {
method2("thread2");
}).start();
}
}
可以看到2个线程没有产生互斥效果,因为method是以当前对象作为锁,而method2是以SynchronizedDemo5.class对象作为锁,锁不同自然就不存在互斥效果。
最后,补充作用在对象方法里面和类静态方法里面,这两个方法和对应作用在对象方法上和作用在类静态方法上是共用一个锁的。也就是这两个方法和前面两个效果一样,大家可以去试试玩一玩,验证一下。