我是java中的多线程的新手。我编写了一些类来测试synchronized的函数。我有一些使用synchronized的方法:
public class ShareUtil {
private static Queue<Integer> queue = new LinkedList<Integer>();
public static synchronized void do1(){
System.out.println("do1");
sleep();
}
public static synchronized void do2(){
System.out.println("do2");
sleep();
}
private static void sleep(){
try {
Thread.sleep(1000*50000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
你可以看到有两种方法使用synchronized,我运行两个线程分别使用这两种方法。
class Run1 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
ShareUtil.do1();
}
}
class Run2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
ShareUtil.do2();
}
}
public class DoMain {
public static void main(String[] args) {
ExecutorService pools = Executors.newCachedThreadPool();
for(int i=0;i<10;i++){
pools.execute(new Run1());
pools.execute(new Run2());
}
pools.shutdown();
}
}
但是,它只是打印“do1”而不是打印“do2”。我想知道为什么?“同步”使用方法的关键是使方法只有一个线程同时使用,但为什么要锁定其他方法?
答案 0 :(得分:5)
重要的关键是 synchronized
锁定对象,而不是方法。
根据https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
每个对象都有一个与之关联的内在锁。按照惯例, 需要对对象进行独占和一致访问的线程 字段必须在访问之前获取对象的内部锁 它们,然后在它们完成后释放内在锁。一个 据说线程拥有它之间的内在锁定 获得了锁并释放了锁。 只要一个帖子拥有一个 内在锁,没有其他线程可以获得相同的锁。另一个 线程在尝试获取锁时会阻塞。
所以,ShareUtil的类内在锁(你的方法是static
,所以这是 intrinsic lock for the Class object associated with the class)当线程T1执行do1()被锁定>,除非T1释放它,否则没有其他线程可以获得此锁。
您调用的方法sleep()
不会释放此锁定,而如果您调用wait()
,则会检查Difference between wait() and sleep()。这就是为什么当另一个线程T2尝试访问do2()时,它必须等待T1的do1()完成(内部锁被释放)。
答案 1 :(得分:1)
因为当您同步static
方法时,它会获取Class
对象上的锁定。一旦线程锁定Class
对象,其他任何线程都不能进入同一类的另一个static
方法。
当线程在锁定时调用sleep()
时,其他任何线程都无法访问其他静态方法,因为该线程不会失去锁的所有权。
因此sleep()
仅导致任一线程执行其方法。您有时也可以do2
打印,但不能do1
。
答案 2 :(得分:0)
Run1()首先获取类ShareUtil上的锁,然后是Run2(),它被阻塞直到Run1()释放锁。一旦Run2()获得锁定,它将打印do2()。同步键保证只有一个线程通过获取类上的锁定来访问该方法(如果方法是静态的)或“object”(如果方法是实例的话)。
答案 3 :(得分:0)
我将代码Thread.sleep(1000*50000);
更改为Thread.sleep(1000*2);
输出结果是不规则的。
do2
do1
do1
do1
do1
do1
do1
do1
do1
do2
do2
do2
do2
do2
do2
do2
do2
do2
do1
我们可以看到这些线程正在等待ShareUtil.class的锁定,并且锁定在Thread.sleep
之后被重新定位,其中一个等待的线程将获取锁定并转向运行。