Currently, virtual threads are never mounted on the scheduler's carriers (FJPool workers) with a monitor held, but custom schedulers could do that.
While ObjectSynchronizer::current_thread_holds_lock (which implements Thread.holdsLock) could easily check whether it is the vthread or its carrier holding the lock in the stack-lock case, using JavaThread::is_lock_owned_current instead of JavaThread::is_lock_owned, that is not the case when monitor->is_entered is called, where the monitor's owner might be the JavaThread itself.
While ObjectSynchronizer::current_thread_holds_lock (which implements Thread.holdsLock) could easily check whether it is the vthread or its carrier holding the lock in the stack-lock case, using JavaThread::is_lock_owned_current instead of JavaThread::is_lock_owned, that is not the case when monitor->is_entered is called, where the monitor's owner might be the JavaThread itself.