多线程 对象锁的问题

LQW_home 2020-08-27 03:57:20
public class Lock02 {
private int i = 0;
public static void main(String[] args) {
Object o = new Object();
Lock02 lock = new Lock02();

new Thread(() -> {
for (int i = 0; i < 100; i++) {
synchronized (o) {
lock.i++;
String name = Thread.currentThread().getName();
System.out.println(lock.i +" :: "+ name);
}
}
}).start();

new Thread(() -> {
for (int i = 0; i < 100; i++) {
synchronized (o) {
lock.i++;
String name = Thread.currentThread().getName();
System.out.println(lock.i +" :: "+ name);
}
}
}).start();
}
}
这是对象锁的测试 为什么打印结果竟然是交错的

1 :: Thread-0
2 :: Thread-0
3 :: Thread-1
4 :: Thread-1
5 :: Thread-1
6 :: Thread-1
7 :: Thread-1

不是获取一个对象锁 方法不执行完 锁不释放吗 这里好像是锁释放了??? 为什么会这样
...全文
9366 4 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
KeepSayingNo 2020-09-03
  • 打赏
  • 举报
回复 1
synchronized 一个是锁代码块,一个是锁方法;你要锁住for整个代码块才能达到你要的效果
KeepSayingNo 2020-09-03
  • 打赏
  • 举报
回复
public class Test2 { private int i = 0; public static void main(String[] args) { Object o = new Object(); Test2 lock = new Test2(); new Thread(() -> { synchronized (o) { for (int i = 0; i < 100; i++) { lock.i++; String name = Thread.currentThread().getName(); System.out.println(lock.i +" :: "+ name); } } }).start(); new Thread(() -> { for (int i = 0; i < 100; i++) { synchronized (o) { lock.i++; String name = Thread.currentThread().getName(); System.out.println(lock.i +" :: "+ name); } } }).start(); } }
大隐藏于寺 2020-08-27
  • 打赏
  • 举报
回复
synchronized有两种使用方式,一种是同步方法(包含静态和非静态方法),一种是同步代码块.JVM在实现时会在常量池的method_info中增加ACC_SYNCHRONIZED标志来标记方法是同步方法,使用了 monitorentermonitorexit 指令来实现同步代码块.当遇到ACC_SYNCHRONIZED或者monitorenter时,也就是说当某个线程A执行同步方法或者同步代码块前,线程A会获取锁对象的Monitor占有权,如果获取不成功,就处于Thread.State.BLOCKED状态,如果获取成功,就执行同步方法或者同步代码块内代码,执行完代码或者遇到异常就释放Monitor占用权.简化的来说就是获取锁->执行代码->释放锁. 回到你的问题,线程0首先抢到锁对象的Monitor占有权,开始执行代码,而同时线程1没有获取到锁对象的Monitor占有权,就处于阻塞状态等待获取到锁对象的Monitor占有权,线程0执行完一次同步代码块中代码后,释放锁对象的Monitor占有权,线程0和线程1开始抢占锁对象的Monitor占有权,然后是抢到锁对象的Monitor占有权的线程继续执行同步代码块中的代码,重复这个过程,从你贴的结果来看,前2次是线程0抢到了,而后面是线程1抢到了.

public class Lock02 {

    public static synchronized void function1() {

    }

    public static void function2() {
    }

    public void function3() {
        synchronized (Lock02.class) {

        }
    }

}

下面是使用javap -c -v 反编译我的Lock02出来的部分信息,因为有点长,有些信息我已经删除了

public class Lock02
   {

  public static synchronized void function1();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED  //注意这里的ACC_SYNCHRONIZED
    Code:
      stack=0, locals=0, args_size=0
         0: return
      LineNumberTable:
        line 13: 0

  public synchronized void function2();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED //注意这里的ACC_SYNCHRONIZED
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 16: 0

  public void function3();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: ldc           #2                  // class Lock02
         2: dup
         3: astore_1
         4: monitorenter //注意这里的monitorenter
         5: aload_1
         6: monitorexit //注意这里的monitorexit 
         7: goto          15
        10: astore_2
        11: aload_1
        12: monitorexit
        13: aload_2
        14: athrow
        15: return
     
}
 


原文: " instructions. When invoking a method for which ACC_SYNCHRONIZED is set, the executing thread enters a monitor, invokes the method itself, and exits the monitor whether the method invocation completes normally or abruptly. During the time the executing thread owns the monitor, no other thread may enter it. If an exception is thrown during invocation of the synchronized method and the synchronized method does not handle the exception, the monitor for the method is automatically exited before the exception is rethrown out of the synchronized method. Synchronization of sequences of instructions is typically used to encode the synchronized block of the Java programming language. The Java Virtual Machine supplies the monitorenter and monitorexit instructions to support such language constructs. Proper implementation of synchronized blocks requires cooperation from a compiler targeting the Java Virtual Machine (§3.14). " 原文 " monitorenter Operation Enter monitor for object Format monitorenter Forms monitorenter = 194 (0xc2) Operand Stack ..., objectref → ... Description The objectref must be of type reference. Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows: If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor. If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count. If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership. Run-time Exception If objectref is null, monitorenter throws a NullPointerException. Notes A monitorenter instruction may be used with one or more monitorexit instructions (§monitorexit) to implement a synchronized statement in the Java programming language (§3.14). The monitorenter and monitorexit instructions are not used in the implementation of synchronized methods, although they can be used to provide equivalent locking semantics. Monitor entry on invocation of a synchronized method, and monitor exit on its return, are handled implicitly by the Java Virtual Machine's method invocation and return instructions, as if monitorenter and monitorexit were used. The association of a monitor with an object may be managed in various ways that are beyond the scope of this specification. For instance, the monitor may be allocated and deallocated at the same time as the object. Alternatively, it may be dynamically allocated at the time when a thread attempts to gain exclusive access to the object and freed at some later time when no thread remains in the monitor for the object. The synchronization constructs of the Java programming language require support for operations on monitors besides entry and exit. These include waiting on a monitor (Object.wait) and notifying other threads waiting on a monitor (Object.notifyAll and Object.notify). These operations are supported in the standard package java.lang supplied with the Java Virtual Machine. No explicit support for these operations appears in the instruction set of the Java Virtual Machine. monitorexit Operation Exit monitor for object Format monitorexit Forms monitorexit = 195 (0xc3) Operand Stack ..., objectref → ... Description The objectref must be of type reference. The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref. The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so. Run-time Exceptions If objectref is null, monitorexit throws a NullPointerException. Otherwise, if the thread that executes monitorexit is not the owner of the monitor associated with the instance referenced by objectref, monitorexit throws an IllegalMonitorStateException. Otherwise, if the Java Virtual Machine implementation enforces the rules on structured locking described in §2.11.10 and if the second of those rules is violated by the execution of this monitorexit instruction, then monitorexit throws an IllegalMonitorStateException. Notes One or more monitorexit instructions may be used with a monitorenter instruction (§monitorenter) to implement a synchronized statement in the Java programming language (§3.14). The monitorenter and monitorexit instructions are not used in the implementation of synchronized methods, although they can be used to provide equivalent locking semantics. The Java Virtual Machine supports exceptions thrown within synchronized methods and synchronized statements differently: Monitor exit on normal synchronized method completion is handled by the Java Virtual Machine's return instructions. Monitor exit on abrupt synchronized method completion is handled implicitly by the Java Virtual Machine's athrow instruction. When an exception is thrown from within a synchronized statement, exit from the monitor entered prior to the execution of the synchronized statement is achieved using the Java Virtual Machine's exception handling mechanism (§3.14). "
qybao 2020-08-27
  • 打赏
  • 举报
回复
你把synchronized(o)放到for前面再试试就知道了。 离开了synchronized(o){}的花括号,锁就自动放开了。你是在for里面拿锁的,所以每次进入循环两个线程都要重新抢锁,抢到锁的就有机会执行一次循环;而如果synchronized(o)放在for循环前,就能保证抢到锁后把for循环执行完才释放锁。这是不一样的。

62,634

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧