Java多线程基础-多线程同步

同步代码

需要同步的情况

  • 多线程并发,有多段代码同时执行时,希望某一段代码执行的过程中CPU不要切换到其他线程上,此时就需要同步。
  • 如果有两段代码是同步进行的,那么同一时间只能执行其中一段,在一段代码没执行结束之前不会执行另外一段代码。

同步代码块操作

  • 使用synchronized关键字加上一个锁对象定义一段代码,这就称为同步代码块

  • 如果多个同步代码块使用同一个锁对象,那么他们就是同步的

  • 同步代码块是锁机制,同一个锁对象,同步代码块是同步的。

  • 锁对象是任意对象,但不能是匿名对象,因为匿名对象不是同一个对象。

  • 当多个代码块使用了同一个锁对象synchronized 锁机制,只有当一个线程把 synchronized 代码块的代码全部执行完之后才能执行该同一锁对象的另一段代码

    • 即该多个代码块是同步的,同一时间只能执行其中一段,执行完之后,才能执行另一段。
    • 锁对象不一致,即不是同步的,会出现抢占线程执行的情况。
  • 具体操作:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    public class SynchronizeTest {
    public static void main(String[] args) {
    Consumer con = new Consumer();
    new Thread(()->{
    while(true)
    con.print1();
    }).start();

    new Thread(()->{
    while(true)
    con.print2();
    }).start();
    }
    }

    class Consumer {
    //定义一个Object对象,作为锁对象
    Object obj = new Object();

    public void print1(){
    //锁机制使用同一个锁对象
    synchronized (obj){
    System.out.print("同");
    System.out.print("步");
    System.out.print("代");
    System.out.print("码");
    System.out.print("块");
    System.out.println();
    }
    }

    public void print2(){
    //锁机制使用同一个锁对象,作为同步代码块
    synchronized(obj){
    System.out.print("多");
    System.out.print("线");
    System.out.print("程");
    System.out.println();
    }
    }
    }

同步方法

  • 使用synchronized关键字修饰一个方法时,该方法中所有代码都是同步的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //同步方法只需在方法上加 synchronized 
    public synchronized void print1(){
    //锁机制使用同一个锁对象
    synchronized (obj){
    System.out.print("同");
    System.out.print("步");
    System.out.print("代");
    System.out.print("码");
    System.out.print("块");
    System.out.println();
    }
    }
  • 非静态同步函数的锁是this

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //同步方法只需在方法上加 synchronized 
    public synchronized void print1(){

    System.out.print("同");
    System.out.print("步");
    System.out.print("代");
    System.out.print("码");
    System.out.print("块");
    System.out.println();

    }
    public void print2(){
    //非静态的同步方法的锁对象是this
    synchronized(this){
    System.out.print("多");
    System.out.print("线");
    System.out.print("程");
    System.out.println();
    }
    }

    此时 这两个方法时 同步的

  • 静态同步函数的锁是字节码对象

    • 静态域随着类的加载而加载,此时会产生该类的字节码对象,所以静态同步方法锁对象不能是this,而是产生的字节码对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public static synchronized void print1(){

    System.out.print("同");
    System.out.print("步");
    System.out.print("代");
    System.out.print("码");
    System.out.print("块");
    System.out.println();

    }
    public static void print2(){
    //静态的同步方法的锁对象是随着类加载而产生的类的字节码对象
    synchronized(Customer.class){
    System.out.print("多");
    System.out.print("线");
    System.out.print("程");
    System.out.println();
    }
    }

同步方法和同步块,哪个是更好的选择

  • 基本原则:同步的范围越小越好
  • 同步块之外的代码是异步执行的,比同步整个方法更有效率。