synchronized的补充

因为之前对synchronized不是很了解,所以前面就照搬尚学堂的课程,这里经过一些学习,稍微有点理解了这个synchronized关键字的用法。下面就说一些synchronized注意事项:

  • 当对某个对象进行锁定的时候
/**
 * 银行账户类
 */
class Account {
   String name;
   float amount;

   public Account(String name, float amount) {
      this.name = name;
      this.amount = amount;
   }
   //存钱
   public  void deposit(float amt) {
      amount += amt;
      try {
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
   //取钱
   public  void withdraw(float amt) {
      amount -= amt;
      try {
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }

   public float getBalance() {
      return amount;
   }
}

/**
 * 账户操作类
 */
class AccountOperator implements Runnable{
   private Account account;
   public AccountOperator(Account account) {
      this.account = account;
   }

   public void run() {
      synchronized (account) {
         account.deposit(500);
         account.withdraw(500);
         System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
      }
   }

  public static void main(String[] args){
        Account account = new Account("zhang san", 10000.0f);
        AccountOperator accountOperator = new AccountOperator(account);

        final int THREAD_NUM = 5;
        Thread threads[] = new Thread[THREAD_NUM];
        for (int i = 0; i < THREAD_NUM; i ++) {
           threads[i] = new Thread(accountOperator, "Thread" + i);
           threads[i].start();
}
  }
}
输出结果:Thread3:10000.0 
        Thread2:10000.0 
        Thread1:10000.0 
        Thread4:10000.0 
        Thread0:10000.0

在AccountOperator 类中的run方法里,我们用synchronized 给account对象加了锁。这时,当一个线程访问account对象时,其他试图访问account对象的线程将会阻塞,直到该线程访问account对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码。

  • 当锁定的是静态类或者静态方法

内存图,对于静态static类和方法是存放在方法去中的,和new出来的类调用方式是不一样的,所以对于static变量和方法其实无论怎么创建,调用的都是同一个地址,静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。

/**
 - 同步线程
 */
class SyncThread implements Runnable {
   private static int count;

   public SyncThread() {
      count = 0;
   }

   public synchronized static void method() {
      for (int i = 0; i < 5; i ++) {
         try {
            System.out.println(Thread.currentThread().getName() + ":" + (count++));
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }

   public synchronized void run() {
      method();
   }
   public static void main(String[] args){
        Account account = new Account("zhang san", 10000.0f);
        AccountOperator accountOperator = new AccountOperator(account);

        final int THREAD_NUM = 5;
        Thread threads[] = new Thread[THREAD_NUM];
        for (int i = 0; i < THREAD_NUM; i ++) {
           threads[i] = new Thread(accountOperator, "Thread" + i);
           threads[i].start();
}
  }
}
输出结果:
        SyncThread1:0 
        SyncThread1:1 
        SyncThread1:2 
        SyncThread1:3 
        SyncThread1:4 
        SyncThread2:5 
        SyncThread2:6 
        SyncThread2:7 
        SyncThread2:8 
        SyncThread2:9
  • 多个线程访问synchronized和非synchronized代码块

一个线程访问一个对象的synchronized代码块时,别的线程可以访问该对象的非synchronized代码块而不受阻塞。

public class TT implements Runnable{

    private int b = 100;

    public synchronized void m1(){
        b = 1000;
        try {
            Thread.sleep(1000);
            System.out.println("b="+b);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    public void m2(){
        System.out.println(b);
    }

    @Override
    public void run() {
        m1();
    }

    public static void main(String[] args){
        TT tt = new TT();
        Thread t = new Thread(tt);
        t.start();  //在这个线程启动之后,会睡眠3秒钟
        try {
            Thread.sleep(500); // 这个睡眠时间是让main线程休眠一会,要保证TT线程已经启动,已经执行到方法里面了
            tt.m2();  //上面线程是启动锁定的线程,然而这个时候main线程还是可以启动。线程已经锁定了,b=1000了,main线程仍然在运行,而且输出
                      //为1000,所以线程虽然可以锁定,但是m2()方法没有锁定,仍然可以访问,所以main线程里面输出的不是100而是1000。
                      //说明一个线程访问一个对象的synchronized代码块时,别的线程可以访问该对象的非synchronized代码块而不受阻塞。
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
输出结果:
        1000
        b=1000

下面是另外一个实例:

public class CpoyTT implements Runnable{

    private int b = 100;

    public synchronized void m1(){
        b = 1000;
        try {
            Thread.sleep(2000);
            System.out.println("b="+b);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    public void m2(){
        try {
            Thread.sleep(1000);
            b = 2000;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        m1();
    }

    public static void main(String[] args){
        CpoyTT tt = new CpoyTT();
        Thread t = new Thread(tt);
        t.start();  //在这个线程启动之后,会睡眠3秒钟

        tt.m2();  //同TT,main线程还是可以访问没有被锁定的方法,如果m2()中的睡眠时间少于m1()中的话,会改变b的值

    }
}
输出结果:b=2000
  • 在用synchronized修饰方法时要注意以下几点:

synchronized关键字不能继承: 虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。这两种方式的例子代码如下:
在子类方法中加上synchronized关键字

class Parent {
   public synchronized void method() { }
}
class Child extends Parent {
   public synchronized void method() { }
}

在子类方法中调用父类的同步方法

class Parent {
   public synchronized void method() {   }
}
class Child extends Parent {
   public void method() { super.method();   }
} 
  1. 在定义接口方法时不能使用synchronized关键字。
  2. 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。

参考博客

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值