No Java 8, eu posso escrever facilmente:
interface Interface1 {
default void method1() {
synchronized (this) {
// Something
}
}
static void method2() {
synchronized (Interface1.class) {
// Something
}
}
}
Vou obter a semântica de sincronização completa que também posso usar nas aulas. No entanto, não posso usar o synchronized
modificador nas declarações do método:
interface Interface2 {
default synchronized void method1() {
// ^^^^^^^^^^^^ Modifier 'synchronized' not allowed here
}
static synchronized void method2() {
// ^^^^^^^^^^^^ Modifier 'synchronized' not allowed here
}
}
Agora, pode-se argumentar que as duas interfaces comportam da mesma maneira, exceto que Interface2
estabelece um contrato no method1()
e no method2()
, que é um pouco mais forte do que o que Interface1
faz. Obviamente, também podemos argumentar que as default
implementações não devem fazer suposições sobre o estado concreto da implementação, ou que essa palavra-chave simplesmente não daria seu peso.
Questão:
Qual é a razão pela qual o grupo de especialistas JSR-335 decidiu não oferecer suporte synchronized
nos métodos de interface?
java
java-8
synchronized
default-method
jsr335
Lukas Eder
fonte
fonte
default synchronized
, mas não necessariamente parastatic synchronized
, embora eu aceite que o último possa ter sido omitido por motivos de consistência.synchronized
modificador pode ser substituído nas subclasses, portanto, só importaria se houvesse algo como métodos padrão finais. (Sua outra pergunta)synchronized
nas superclasses, removendo efetivamente a sincronização. Não me surpreenderia que não apoiarsynchronized
e não apoiarfinal
esteja relacionado, talvez por causa de herança múltipla (por exemplo, herançavoid x()
esynchronized void x()
etc.). Mas isso é especulação. Estou curioso sobre uma razão autorizada, se houver uma.super
que requer uma reimplementação completa e possível acesso a membros privados. Aliás, há uma razão para que esses métodos sejam chamados de "defensores" - eles estão presentes para facilitar a adição de novos métodos.Respostas:
Embora a princípio possa parecer óbvio que alguém queira apoiar o
synchronized
modificador nos métodos padrão, acontece que isso seria perigoso e, portanto, era proibido.Métodos sincronizados são uma abreviação de um método que se comporta como se todo o corpo estivesse encerrado em um
synchronized
bloco cujo objeto de bloqueio é o receptor. Pode parecer sensato estender essa semântica para métodos padrão também; afinal, eles também são métodos de instância com um receptor. (Observe que ossynchronized
métodos são inteiramente uma otimização sintática; eles não são necessários, são apenas mais compactos que os correspondentessynchronized
bloco . Há um argumento razoável a ser feito de que essa foi uma otimização sintática prematura em primeiro lugar e que métodos sincronizados causam mais problemas do que resolvem, mas esse navio navegou há muito tempo.)Então, por que eles são perigosos? Sincronização é sobre bloqueio. O bloqueio é sobre a coordenação do acesso compartilhado ao estado mutável. Cada objeto deve ter uma política de sincronização que determine quais bloqueios guardam quais variáveis de estado. (Consulte Concorrência Java na prática , seção 2.4.)
Muitos objetos usam como política de sincronização o Java Monitor Pattern (JCiP 4.1), no qual o estado de um objeto é protegido por seu bloqueio intrínseco. Não há nada de mágico ou especial nesse padrão, mas é conveniente, e o uso da
synchronized
palavra-chave em métodos assume implicitamente esse padrão.É a classe que possui o estado que determina a política de sincronização desse objeto. Mas as interfaces não possuem o estado dos objetos nos quais eles estão misturados. Portanto, o uso de um método sincronizado em uma interface pressupõe uma política de sincronização específica, mas que você não tem base razoável para supor; portanto, pode ser que o uso da sincronização não fornece segurança adicional ao thread (você pode estar sincronizando na trava errada). Isso lhe daria a falsa sensação de confiança de que você fez algo sobre segurança de encadeamento, e nenhuma mensagem de erro informa que você está assumindo a política de sincronização errada.
Já é bastante difícil manter consistentemente uma política de sincronização para um único arquivo de origem; é ainda mais difícil garantir que uma subclasse adira corretamente à política de sincronização definida por sua superclasse. Tentar fazê-lo entre essas classes fracamente acopladas (uma interface e as possivelmente muitas classes que a implementam) seria quase impossível e altamente suscetível a erros.
Dados todos esses argumentos contra, qual seria o argumento? Parece que a maior parte deles faz com que as interfaces se comportem mais como características. Embora esse seja um desejo compreensível, o centro de design para métodos padrão é a evolução da interface, não "Traits--". Onde os dois poderiam ser consistentemente alcançados, nos esforçamos para fazê-lo, mas onde um está em conflito com o outro, tivemos que escolher a favor do objetivo principal do design.
fonte
synchronized
modificador de método apareceu na saída do javadoc, levando as pessoas a pensarem que era parte da especificação. Isso foi corrigido no JDK 1.2. Mesmo que apareça em um método público, osynchronized
modificador faz parte da implementação, não do contrato. (Raciocínio e tratamento similar ocorreu para onative
modificador.)synchronized
e encadear componentes seguros e você tinha um programa quase encadeado. O problema era que isso geralmente funcionava bem, mas era quebrado de maneiras surpreendentes e quebradiças. Concordo que entender como o bloqueio funciona é a chave para aplicativos robustos.synchronized(this) {...}
permitido em umdefault
método? (Como mostrado na pergunta de Lukas.) Isso não permite que o método padrão seja o proprietário do estado da classe de implementação também? Não queremos impedir isso também? Precisamos de uma regra FindBugs para encontrar os casos pelos quais desenvolvedores desinformados fazem isso?synchronized(vector)
. Se você quer ser seguro, nunca deve usar um objeto público (comothis
ele mesmo) para bloquear.resultado:
(desculpe por usar a classe pai como exemplo)
do resultado, poderíamos saber que o bloqueio da classe pai pertence a todas as subclasses, os objetos SonSync1 e SonSync2 têm um bloqueio de objeto diferente. todo bloqueio é independência. Portanto, nesse caso, acho que não é perigoso usar um sincronizado em uma classe pai ou em uma interface comum. alguém poderia explicar mais sobre isso?
fonte