Le modificateur synchronized s'applique également à des blocs de codes critiques au sein des méthodes.

L'utilisation de cette technique peut être opportune lorsqu'une méthode ne requiert pas une synchronisation globale.

public void uneMethode(){
  //...
  synchronized(objet){
    //code synchroniée
  }
  //...
}

L'objet de synchronisation peut être une référence à l'instance de la classe courante, ou à tout autre objet présent dans la machine virtuelle.

synchronized(this){
  //Bloc de code
}

public Object verrou = new Object();
//...
synchronized(verrou){
  //Bloc de code
}

Par exemple, il est possible d'obtenir une synchronisation sur l'objet qu'il est nécessaire de modifier.

public class Bloc implements Runnable{
  private String adresse;
  public Bloc(String adresse){
    this.adresse = adresse;
  }
  public String lireURL(String adresse) {
    //Lecture d'une adresse URL...
  }
  public void traitement(DocumentXML doc) {
    String sourceXML = lireURL(this.adresse);
    synchronized (doc) {
      doc.chargerDocument(sourceXML);
    }
    enregistrerFichier(sourceXML, 
               this.adresse.replaceFirst("http:/", "c:"));
    //...
  }
  public void run(){
    DocumentXML doc = new DocumentXML();
    traitement(doc);
    //...
  }
  public static void main(String[] args){
    for(int i = 0; i < 3; i++){
      Bloc bloc = new Bloc("http://site.com/fichier" + i + ".xml");
      Thread t = new Thread(bloc);
      t.start();
    }
  }
}

public class DocumentXML {
  private Document document;
  public DocumentXML() {
    this.document = null;
  }
  public synchronized void chargerDocument(String source) {
    try {
      DocumentBuilderFactory fabrique = DocumentBuilderFactory.newInstance();
      fabrique.setNamespaceAware(true);
      fabrique.setValidation(true);
      DocumentBuilder constructeur = fabrique.newDocumentBuilder();
      this.document = constructeur.parse(new File(source));
    }
    catch(Exception e) {
      e.printStackTrace();
    }
  }
  public synchronized Document getDocument() {
    return document;
  }
  //...
}

Une portion de code synchronisée peut appeler une autre méthode synchronisée puisqu'un seul thread possède le moniteur sur l'ensemble du code synchronisé de l'objet courant.

Toutes les méthodes d'instance et les méthodes statiques d'une classe peuvent être synchronisées. Dans le cas d'un accès synchronisé entre une méthode d'instance et une méthode statique, le bloc accédant à la méthode statique doit être synchronisé par rapport à un objet commun, tel que l'objet Class de la classe courante.

public void methodeInstance(){
  synchronized(this.getClass()){ //Si l'objet est disponible
  //synchronized(NomClasse.class){
  methodeStatique();
  }
  //...
}

public static void methodeStatique(){
  //...
}

La sécurisation de l'accès à une champ statique doit passer par la création d'un moniteur auquel toutes les instances de la classe courante peuvent se référer. L'objet Class retourné par la méthode getClass() est tout à fait indiquer pour servir de verrou à ce moniteur.

public class Exemple {
  private static int acces = 0;
  private int valeur;
  public Exemple(){
    this.valeur = 0;
  }
  public synchronized void setValeur(int valeur) {
    this.valeur = valeur;
    synchronized (this.getClass()) {
      acces++;
    }
  }
  public int getValeur() {
    synchronized (this.getClass()) {
      acces++;
    }
    return this.valeur;
  }
  public int getAcces() {
    return acces;
  }
}

Les méthodes statiques doivent être synchronisées

public class Compteur implements Runnable {
  private static int compteur = 1;

  public synchronized static int getCompteur() {
    int etat = compteur;
    try {
      Thread.sleep((long) (Math.random() * 1000));
      compteur++;
    }
    catch (InterruptedException x) {
    }
    return etat;
  }
  private void afficher(int etat) {
    System.out.println(Thread.currentThread().getName() 
                     + " : compteur = " + (compteur - 1) 
                     + " / " + etat);
  }
  public void run() {
    this.afficher(getCompteur());
  }
  public static void main(String[] args) {
    try {
      Compteur compteur = new Compteur();
      for (int i = 0; i < 12; i++) {
        Thread t = new Thread(compteur, "Thread-" + i);
        t.start();
        Thread.sleep((long) (Math.random() * 1000));
      }
    }
    catch (InterruptedException x) {
    }
  }
}

/* Affiche sans synchronized :
Thread-0 : compteur = 1 / 1
Thread-1 : compteur = 2 / 2
Thread-2 : compteur = 3 / 3
Thread-4 : compteur = 4 / 4
Thread-3 : compteur = 5 / 4
Thread-5 : compteur = 6 / 6
Thread-7 : compteur = 7 / 7
Thread-6 : compteur = 8 / 7
Thread-9 : compteur = 9 / 8
Thread-8 : compteur = 10 / 8
Thread-10 : compteur = 11 / 10
Thread-11 : compteur = 12 / 11

* Affiche avec synchronized :
Thread-0 : compteur = 1 / 1
Thread-1 : compteur = 2 / 2
Thread-2 : compteur = 3 / 3
Thread-3 : compteur = 4 / 4
Thread-4 : compteur = 5 / 5
Thread-5 : compteur = 6 / 6
Thread-6 : compteur = 7 / 7
Thread-7 : compteur = 8 / 8
Thread-8 : compteur = 9 / 9
Thread-9 : compteur = 10 / 10
Thread-10 : compteur = 11 / 11
Thread-11 : compteur = 12 / 12