synchronized关键字
主要用于修饰以下三种情况
一,修饰普通方法。
private synchronized void add(){}
eg
public class TestSync {
private int count;
public static void main(String[] args) {
final TestSync testSync=new TestSync();
new Thread(new Runnable() {
@Override
public void run() {
testSync.add();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
testSync.add();
}
}).start();
}
private synchronized void add(){
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+"--"+count++);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出结果
image.png
可以看到第一个线程执行完成了才会执行第二个线程。如果没有用synchronized修饰的话,结果会是两个线程随机调度。可自行实践。
二,修饰代码块
在某些情况下,我们编写的方法体可能比较大,同时存在一些耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失。
我们把add()方法改一下
private void add(){
//其他操作省略....
synchronized (this){
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+"--"+count++);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
得到的结果和上面例子的结果是一样的。假如该方法只有一个for循环。像这样
private void add(){
synchronized (this){
for (int i = 0; i <10 ; i++) {
System.out.println(Thread.currentThread().getName()+"--"+count++);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
这样和直接修饰方法是没有区别的。
三,修饰静态方法
静态方法是类方法,使用synchronized关键字修饰静态方法得到的类锁。类锁和对象锁不互斥,也就是说多个线程分别调用
某个类的同步静态方法和同步普通方法,不会是同步的。
public class TestSync {
public static void main(String[] args) {
final TestSync testSync=new TestSync();
new Thread(new Runnable() {
@Override
public void run() {
delete();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
testSync.add();
}
}).start();
}
private synchronized void add(){
//其他操作省略....
for (int i = 0; i <10 ; i++) {
System.out.println("线程"+Thread.currentThread().getName()+"执行次数--"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized static void delete(){
for (int i = 0; i <10 ; i++) {
System.out.println("线程"+Thread.currentThread().getName()+"执行次数--"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果
image.png
多个线程调一个同步静态方法,是互斥的。
public static void main(String[] args) {
final TestSync testSync=new TestSync();
final TestSync testSync2=new TestSync();
new Thread(new Runnable() {
@Override
public void run() {
delete();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
delete();
}
}).start();
}
private synchronized static void delete(){
for (int i = 0; i <10 ; i++) {
System.out.println("线程"+Thread.currentThread().getName()+"执行次数--"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果如下图,得出多
image.png
上面我们讲到对象锁,什么叫对象锁呢,也就是一个对象只有一把锁,拿到这个锁才能调用被锁的方法,我们试试两个对象
public class TestSync {
public static void main(String[] args) {
final TestSync testSync=new TestSync();
final TestSync testSync2=new TestSync();
new Thread(new Runnable() {
@Override
public void run() {
testSync2.add();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
testSync.add();
}
}).start();
}
private synchronized void add(){
//其他操作省略....
for (int i = 0; i <10 ; i++) {
System.out.println("线程"+Thread.currentThread().getName()+"执行次数--"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized static void delete(){
for (int i = 0; i <10 ; i++) {
System.out.println("线程"+Thread.currentThread().getName()+"执行次数--"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
结果
image.png
结果很明显,虽然这个add方法是同步的,但是我new了两个对象,调用的时候,并不会互斥。
总结:
synchronized关键一般代表的是对象锁,一个对象只有一把锁,在同一个对象内,多个线程调用一个synchronized修饰的非静态方法会互斥。
如果修饰的是静态方法,则得到的类锁,此时该方法相对于整个类都是同步的,对象锁无关且不互斥。
参考 https://blog.csdn.net/javazejian/article/details/72828483
网友评论