O snippet synchronized(X.class)
usa a instância da classe como um monitor. Como há apenas uma instância de classe (o objeto que representa os metadados da classe em tempo de execução), um thread pode estar neste bloco.
Com synchronized(this)
o bloqueio fica guardado pela instância. Para cada instância, apenas um thread pode entrar no bloco.
synchronized(X.class)
é usado para garantir que haja exatamente um Thread no bloco. synchronized(this)
garante que haja exatamente um encadeamento por instância. Se isso torna o código real no bloco seguro para thread, depende da implementação. Se mutate, apenas o estado da instância synchronized(this)
é suficiente.