ciao, ho problemi a capire se ho capito bene la parola chiave synchronized in java

vi spiego meglio, ho questa classe
codice:
public class BankAccount { // Classe dell’oggetto condiviso
	private double balance;

	public BankAccount(double balance) {
		this.balance = balance;
	}

	public synchronized double getBalance() {
		return balance;
	}

	public synchronized void deposit(double amount) {
		balance += amount;
	}

	public synchronized void withdraw(double amount) throws RuntimeException {
		if (amount > balance) {
			throw new RuntimeException("Overdraft");
		}
		balance -= amount;
	}

	public synchronized void transfer(double amount, BankAccount destination) {
		this.withdraw(amount);
		destination.deposit(amount);
	}
}
che gestisce un conto bancario con tutti i metodi sincronizzati

questa invece è la classe che gestisce un thread che si occupa di fare operazioni di scambio tra 2 conti ( per testare)


codice:
package esercizio1;

public class Transfer extends Thread {

	private BankAccount a;
	private BankAccount b;
	private double d;

	public Transfer(BankAccount a, BankAccount b, double d) {
		this.setA(a);
		this.setB(b);
		this.d = d;
	}

	@Override
	public void run() {
		a.transfer(d, b);
	}

	public BankAccount getA() {
		return a;
	}

	public void setA(BankAccount a) {
		this.a = a;
	}

	public BankAccount getB() {
		return b;
	}

	public void setB(BankAccount b) {
		this.b = b;
	}

}
questo invece è il main
codice:
public class Main {
	public static void main(String[] args) {
		BankAccount a = new BankAccount(100.0);
		BankAccount b = new BankAccount(100.0);
		Transfer x = new Transfer(a, b, 50.0);
		Thread t = new Thread(x);
		Transfer y = new Transfer(b, a, 10.0);
		Thread t2 = new Thread(y);
		t.start();
		t2.start();
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("Account a has $" + x.getA().getBalance());
		System.out.println("Account b has $" + x.getB().getBalance());
	}
}

Le mie domande sono le seguenti:

nel main ho messo uno sleep dopo lo start dei thread e il print, l' output è sempre lo stesso ed è quello corretto mentre senza lo sleep a volte mi da risultati errati: è causato dal fatto che magari la stampa viene fatta prima che il thread finisca di lavorare?

Se è come ho detto sopra allora per stare al sicuro dovrei usare il metodo join() al posto dello sleep() giusto? Oltre il fatto che il join libera eventuali lock sul thread main giusto?

Ora la domanda che mi pone più dubbi:
Il metodo transfer chiama a sua volta 2 metodi sincronizzati invocati su istanze diverse.
io so che synchronized è re-entrant cioè che se un metodo sincronizzato chiama un altro metodo sincronizzato sullo stesso lock può andare avanti senza condizioni di stallo quindi mi chiedo io eseguendo i 2 metodi sincronizzati dentro il metodo transfer implicitamente chiedo già di avere entrambi i lock sui due conti quindi è inutile mettere la parola chiave synchronized sul metodo transfer? così facendo il thread che esegue transfer si blocca solo dentro il metodo transfer quando arriva a chiedere i lock per invocare gli altri due sottometodi giusto?

E' giusto/meglio togliere synchronized dal metodo transfer? Quello che penso è che senza dichiarare esplicitamente transfer come synchronized viene sempre eseguito e si può bloccare a
codice:
this.withdraw(amount);
se non si ha il lock su l' istanza this come anche si può bloccare solo dopo quando viene eseguito
codice:
destination.deposit(amount);
e non si ha il lock sull' altra istanza... Insomma non viene bloccato a priori ma può iniziare a lavorare per bloccarsi poi...
Se ho ragione visto che i metodo contengono poche righe dichiare metodi synchronized o il codice del metodo synchronized sui conti è uguale?

Come avete capito ho un pò di confusione, potete illuminarmi su cosa cambia nei diversi casi?
Grazie in anticipo,
Luca