当一个线程访问同步代码块时,首先是需要得到锁才能执行

其实“锁”本身是个对象,synchronized这个关键字并不是“锁”。
从语法上讲,Java中的每个对象都可以看做一把锁,在HotSpot JVM实现中,锁有个专门的名字:监视器(Monitor)。
Monitor对象存在于每个Java对象的对象头中,这也是为什么Java中任意对象可以作为锁的原因,有关Monitor后续会详细介绍,有了这些概念看下面这张图应该就容易多了。

一个对象中有两个实例方法同时被synchronized,则同一个对象,调用这两个方法时,只能同时执行一个。
原因:翻看相关书籍,发现jvm在执行方法以前,如果发现该方法前面有对象的synchronized关键字,就现在该对象的ID上加锁,当其他线程执行同时执行这个方法时,会检测改对象ID上是否加锁,如果加锁时就等待锁释放。

Java Thread acquires an object level lock when it enters into an instance synchronized java method and acquires a class level lock when it enters into static synchronized java method.

如果是静态的方法呢?
https://stackoverflow.com/questions/15438727/if-i-synchronized-two-methods-on-the-same-class-can-they-run-simultaneously
静态方法使用.class上的锁。如果class A {static synchronized void m() {} },然后 new A().m()会锁住新的A()对象,此时别人调用A.m()需要的是A.class的锁但现在没有这样的锁,所以不会互相阻塞。所以永远不要实例化对象来调用静态方法。
Have in mind that static methods use lock on .class object. So if you have class A {static synchronized void m() {} }. And then one thread calls new A().m() it acquires lock on new A() object. If then another thread calls A.m() it ENTERS THE METHOD NO PROBLEM because what it looks for is lock on A.class object while NO THREADS possess this kind of lock. So even though you declared method synchronized it actualy IS accessed by two different threads AT THE SAME TIME. Thus: never use object references to call static methods

Putting synchronized on an instance method means that the thread has to acquire the lock (the “intrinsic lock”) on the object instance that the method is called on before the thread can start executing any code in that method.把synchronized放一个实例方法上意味着,该线程必须获取关于对象实例锁(以下简称“本征锁”),该方法被调用之前线程可以开始在该方法中执行的任何代码。


5:35 PM Friday, October 15, 2021更新

有一个单例类FTPClientUtils里面全是静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


public class FTPClientUtils {

public static synchronized void getInstance()

public static synchronized void switchToDir()

public static synchronized void getOutputStream()

public static synchronized void Upload()

public static synchronized void finish()

}

有一个Service里有一个上传文件的方法

1
2
3
4
5
6
7
8
9
public  void upload(){

FTPClientUtils.getInstance();//确认ftpclient实例可用
FTPClientUtils.switchToDir();//切换至目录
FTPClientUtils.getOutputStream();//创建文件流
FTPClientUtils.UploadStream();//传输流
FTPClientUtils.finishStream();//切断文件流

}

所有静态方法都在竞争同一个class锁

问题是:如果在Upload()过程中,执行其他方法。在Upload()结束后放下锁,希望finish()立刻得到锁,结果被外面的getInstance()抢到了,又因为没关闭的文件流导致getInstance()阻塞,造成了死锁。

解决办法一:把service中的upload()加代码块锁

1
2
3
4
5
6
7
8
9
10
11
public  void upload(){

synchronized(FTPClientUtils.class){
FTPClientUtils.getInstance();//确认ftpclient实例可用
FTPClientUtils.switchToDir();//切换至目录
FTPClientUtils.getOutputStream();//创建文件流
FTPClientUtils.UploadStream();//传输流
FTPClientUtils.finishStream();//切断文件流
}

}

解决办法二:在FTPClientUtils中新建一个静态加锁方法,并把五个方法五合一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public  class FTPClientUtils  {

public static synchronized void getInstance()

public static synchronized void switchToDir()

public static synchronized void getOutputStream()

public static synchronized void Upload()

public static synchronized void finish()

public static synchronized void upload(){

getInstance();
switchToDir();
getOutputStream();
Upload();
finish();

}

}