synchronized是Java提供的一个并发控制的关键字,作用于对象上。主要有两种用法,分别是同步方法和同步代码块,保证了代码的原子性和可见性以及有序性,但是不会处理重排序以及代码优化的过程。
//同步方法
public synchronized void sms() throws InterruptedException {
System.out.println("Sms....");
}
public static void main(String[] args) {
while (true){
//同步代码块
synchronized ("obj"){
......
}
}
}
在同步方法中,Synchronized关键字实现同步的原因是使用了flag标记ACC_SYNCHRONIZED,当调用同步方法时,调用指令会检查方法的ACC_SYNCHRONIZED访问标志是否设置,设置了,执行线程会先持有同步锁,然后执行方法,最后方法完成释放锁。
用以下代码测试一下,看看字节码指令
public class A {
public static synchronized void test(){
}
public static void main(String[] args) {
test();
}
}
字节码指令如下
//由于太多,部分省略,直接看下面
public static synchronized void test();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED //标志位
Code:
stack=0, locals=0, args_size=0
0: return
LineNumberTable:
line 4: 0
.....
而在同步代码块中,Synchronized关键字实现同步的原因是使用了monitorenter和monitorexit 来进行同步处理。
public class A {
public static void main(String[] args) {
synchronized ("obj"){
}
}
}
字节码指令如下
//由于太多,部分省略,直接看下面
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #2 // String obj
2: dup
3: astore_1
4: monitorenter //看这里
5: aload_1
6: monitorexit //还有这里
7: goto 15
10: astore_2
11: aload_1
.......
Synchronized关键字还具有锁重入功能
public class Test06 {
public static void main(String[] args) {
new Service().a();
}
}
class Service{
synchronized void a(){
System.out.println("a");
b();
}
synchronized void b(){
System.out.println("b");
c();
}
synchronized void c(){
System.out.println("c");
d();
}
synchronized void d(){
System.out.println("d");
}
}
在使用synchronized关键字时,当一个线程得到一个对象锁后,再次请求该对象锁是可以得到该对象锁的。简单来说,在一个synchronized方法/块的内部调用本类的其它synchronized方法/块,是可以得到锁的。
锁重入也支持继承环境,看看下面的示范
/**
* @Auther: 罗罗
*/
public class Test06 {
public static void main(String[] args) {
new Mythread().start();
}
}
class Father{
int i = 10;
synchronized void f(){
System.out.println("father"+i--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Child extends Father{
synchronized void c(){
while (i > 0){
System.out.println("child"+i--);
try {
Thread.sleep(1000);
super.f();
} catch (InterruptedException e) {
}
}
}
}
class Mythread extends Thread{
@Override
public void run() {
new Child().c();
}
}
"C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:D:\IntelliJ IDEA\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar=53208:D:\IntelliJ IDEA\IntelliJ IDEA 2018.2.4\bin" -"Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program bin\rep\org\apache\xmlbeans\xmlbeans\3.0.1\xmlbeans-3.0.1.jar;D:\apache-maven-3.5.4-bin\rep\joda-time\joda-time\2.10.5\joda-time-2.10.5.jar" Test06
child10
father9
child8
father7
child6
father5
child4
father3
child2
father1
Process finished with exit code 0
可以看出,子类完全可以通过锁重入调用父类的同步方法的
出现异常,Synchronized锁也会自动释放
public class Test06 {
public static void main(String[] args) {
Father father = new Father();
new Mythread1(father).start();
new Mythread2(father).start();
}
}
class Father{
int i = 10;
synchronized void f(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("f "+i--);
}
synchronized void f1(){
int c =2/0;
System.out.println("f1 "+c);
}
}
class Mythread1 extends Thread{
private Father father;
public Mythread1(Father father){
this.father = father;
}
@Override
public void run() {
father.f1();
}
}
class Mythread2 extends Thread{
private Father father;
public Mythread2(Father father){
this.father = father;
}
@Override
public void run() {
father.f();
}
}
"C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:D:\IntelliJ IDEA\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar=53587:D:\IntelliJ IDEA\IntelliJ IDEA 2018.2.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program time\2.10.5\joda-time-2.10.5.jar" Test06
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at Father.f1(Test06.java:26)
at Mythread1.run(Test06.java:46)
f 10
Process finished with exit code 0