Одна вещь, не упомянутая в других отличных ответах, которые уже даны, это разница между code1 и code2. В code1 синхронизация выполняется для экземпляра объекта, в котором обнаружен код, а в code2 - для конкретного объекта блокировки внутри объекта.
Если в охватывающем классе есть только два синхронизированных блока, между ними нет функциональной функциональной разницы, но учтите это:
class CodeOneClass {
...
synchronized(this) { // or merely "synchronized" - it defaults to this
first protected code block
}
...
synchronized(this) {
second protected code block
}
...
}
class CodeTwoClass {
...
Object lock1 = new Object();
synchronized(lock1) {
first protected code block
}
...
Object lock2 = new Object();
synchronized(lock2) {
second protected code block
}
...
}
Если два потока пытаются использовать один и тот же экземпляр CodeOneClass, только один из них может находиться в в одном из двух защищенных блоков кода одновременно.
Но со второй идиомой вы можете сказать, что один поток находится в первом защищенном блоке безопасно, а другой - в другом. Обратите внимание, что если бы блокировки были одинаковыми (обе синхронизировались на одном и том же объекте блокировки), поведение было бы таким же, как и у первого.
Есть и другие различия. Некоторые авторы начинают указывать на проблемы с синхронизацией (this) - я бы указал вам на другой пост здесь на SO:
Избежать синхронизации (этого) в Java?
Я настоятельно рекомендую прочитать его и три сообщения, на которые он ссылается.