Poxa, é que é meio difícil te dar um exemplo sem algumas páginas de textos.
Concorrência é mesmo um assunto complicado. =/
A vantagem do Semáforo sobre o Lock é que ele permite que várias threads entrem num trecho semaforizado antes de haver a trava. É muito útil para obtenção de tokens. Por exemplo, considere que seu banco de dados pode rodar até 5 statements simultâneos. Você poderia ter um semáforo que permitisse 5 threads solicitarem um statement:
private Semaphore semaphore = new Semaphore(5);
public Statement createStatement() {
semaphore.acquire();
return connection.createStatement();
}
public void release() {
semaphore.release();
}
O código do exemplo está rudimentar. Na prática haveriam mais alguns mapas, e coisas do tipo.
O lock tem mais tipos de sincronização que o bloco sinchronized simples. Dê uma olhada na ReadWriteLock, que é um desses tipos diferentes. Isso permite que você tenha sincronização mais flexível.
A vantagem do synchronized é, definitivamente, sua simplicidade e a garantia de que você nunca esquecerá de liberar o bloco sincronizado. Note que para o uso correto, o lock deve estar num bloco try...finally. Isso é simplesmente desnecessário com o synchronized.