synchronized的补充

来源:互联网 发布:淘宝界面 购物车代码 编辑:程序博客网 时间:2024/06/08 03:14

因为之前对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代码块来进行同步。

参考博客

原创粉丝点击