android开发之synchronized的用法

  1. 简介
    synchronized是Java中的的关键字,是一种同步锁,可以修饰以下几种
    1.1>:类
    作用的范围是:synchronized后边括号括起来的部分
    作用的对象是:这个类当中所有的对象
    1.2>:静态方法
    作用的范围是:整个静态方法
    作用的对象是:这个类当中的所有对象
    1.3>:代码块:被修饰的代码块称为同步代码块
    作用的范围是:大括号括起来的部分
    作用的对象是:调用这个代码块的对象
    1.4>:方法:被修饰的方法称为同步方法
    作用的范围是:整个方法
    作用的对象是:调用这个方法的对象
  2. 实例分析
    2.1>: synchronized修饰的代码块
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity" ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SyncThread syncThread = new SyncThread() ;
        Thread thread1 = new Thread(syncThread , "SyncThread1") ;
        Thread thread2 = new Thread(syncThread , "SyncThread2") ;
        thread1.start();
        thread2.start();
    }

    class SyncThread implements Runnable{
        private int count ;
        public SyncThread(){
            count = 0 ;
        }

        @Override
        public void run() {
            synchronized (this){
                for (int i = 0; i < 5; i++) {
                    try {
                        Log.d("mainActivity-->", Thread.currentThread().getName() + ":" + (count++)) ;
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        public int getCount(){
            return count ;
        }
    }
}

上边运行结果是

//SyncThread2:0
//SyncThread2:1
//SyncThread2:2
//SyncThread2:3
//SyncThread2:4
//SyncThread1:5
//SyncThread1:6
//SyncThread1:7
//SyncThread1:8
//SyncThread1:9

由以上可知:
当两个线程thread1和thread2访问 "同一个syncThread对象" 中的synchronized代码块,同一时刻只能一个执行线程,另一个受阻,必须等当前线程执行完这个代码块后才能执行,thread1在执行synchronized代码块时会锁定当前对象,只有执行完该代码块后才能释放对象锁,下一个线程才能执行并锁定该对象

我们现在把

Thread thread1 = new Thread(syncThread , "SyncThread1") ;
Thread thread2 = new Thread(syncThread , "SyncThread2") ;

这两句修改为

Thread thread1 = new Thread(new SyncThread() , "SyncThread1") ;
Thread thread2 = new Thread(new SyncThread() , "SyncThread2") ;

运行结果是

//SyncThread1:0
//SyncThread2:0
//SyncThread1:1
//SyncThread2:1
//SyncThread1:2
//SyncThread2:2
//SyncThread1:3
//SyncThread2:3
//SyncThread1:4
//SyncThread2:4

现象:
thread1和thread2同时在执行
原因:
synchronized修饰代码块时,只锁定代码块中的对象,一个对象只有一个锁 [ lock ] 与之关联,而上边代码等同于

SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();

这个时候创建了2个对象,分别为 syncThread1何syncThread2,线程thread1执行syncThread1对象中对应synchronized的代码,线程thread2执行syncThread2中对应的synchronized对象,我们都知道synchronized锁定的是对象,这个时候会有两把锁分别锁定syncThread1对象和syncThread2对象,而这两把锁互补干扰,所以两个线程可以同时执行

代码:https://github.com/shuai999/ThreadDemo


2.2:当一个线程访问一个对象的synchronized(this)代码块时,其他线程也可以访问该对象的非synchronized(this)代码块,并且不受阻塞
多个线程访问同一个对象的synchronized(this)代码块和非synchronized(this)代码块,示例如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Counter counter = new Counter() ;
        //参数一:Runnable的实现类 参数二:线程名
        Thread thread1 = new Thread(counter , "A") ;
        Thread thread2 = new Thread(counter , "B") ;
        thread1.start();
        thread2.start();
        
    }

    class Counter implements Runnable{

        private int count ;
        public Counter(){
            count = 0 ;
        }

        public void countAdd(){
            //synchronized代码块
            synchronized (this){
                for (int i = 0; i < 5; i++) {
                    try {
                        Log.d("threadName--->" , Thread.currentThread().getName() +":" + (count++)) ;
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }


        //非synchronized代码块,未对count进行读写操作,所以可以不用synchronized
        public void printAdd(){
            for (int i = 0; i < 5; i++) {
                try {
                    Log.d("threadName--->" , Thread.currentThread().getName() + "count:" + count) ;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }


        @Override
        public void run() {
            String threadName = Thread.currentThread().getName() ;
            if (threadName.equals("A")){
                countAdd();
            }else if (threadName.equals("B")){
                printAdd();
            }
        }
    }

//    Bcount:0
//    A:0
//    Bcount:1
//    A:1
//    Bcount:2
//    A:2
//    Bcount:3
//    A:3
//    Bcount:4
//    A:4
    /* 以上是运行结果 */
    /* 由结果可知,countAdd()方法是synchronized代码块,printAdd()不是synchronized代码块。
       当一个线程访问一个对象的synchronized代码块时,其他的线程可以访问该对象的非synchronized代码块而不受阻塞*/
}

具体代码已上传至github:
https://github.com/shuai999/ThreadDemo3


2.3:指定给某个对象加锁
代码如下:

/**
 * @author : Created by ces
 * @date: on 2018/1/28.
 * @function: 指定给某个对象加锁
 */
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Account account = new Account("zhangsan" , 10000.0f) ;
        AccountOperator operator = 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(operator , "Thread" + i);
            threads[i].start();
        }
    }

    public class AccountOperator implements Runnable{

        private Account account ;
        public AccountOperator(Account account){
            this.account = account ;
        }

        @Override
        public void run() {
            synchronized (account){
                //存钱500
                account.deposit(500);
                //取钱500
                account.withDraw(500);
                Log.e("cesAccount--->", Thread.currentThread().getName() + ":" + account.getBalance()) ;
                //运行结果如下
//                Thread0:10000.0
//                Thread1:10000.0
//                Thread2:10000.0
//                Thread3:10000.0
//                Thread4:10000.0
            }
        }
    }
}
/**
 * @author : Created by ces
 * @date: on 2018/1/28.
 * @function: 指定给某个对象加锁
 */
public 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 ;
    }

}

在AccountOperator类中,我们给account对象加了锁,这时当一个线程访问account对象时,其他线程就会阻塞,直到该线程访问account对象结束,也就是说谁拿到那个锁,谁就可以运行它所控制的那段代码。
具体代码已上传至github
https://github.com/shuai999/ThreadDemo4.git


2.4 修饰一个方法

格式:public synchronized void method(){//todo};

synchronized修饰方法和代码块类似,只是作用范围不一样:
修饰的代码块是大括号括起来的范围;
修饰的方法是整个函数;

在用synchronized修饰方法时注意以下几点:
1.synchronized关键字不能被继承
1.1 :如果在父类中某个方法使用了synchronized,而在子类中覆盖此方法,在子类中这个方法默认并不是同步的,必须显式的在方法中加synchronized才可以;
1.2:还可以在子类方法中调用父类中相应的方法,这样虽然子类中方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了,这两种方式代码如下:

在子类方法上边加上synchronized:
    class Parent{
        public synchronized void method(){}
    }
   class Child extends Parent{
        public synchronized void method(){}
    }
在子类方法中调用父类的同步方法:
    class Parent{
        public synchronized void method(){}
    }
    class Childe extends Parent{
        public void method(){
            super.method() ;
        }
    }

注意:
1>:在定义接口方法时不能使用synchronized
2>:在构造方法中不能用synchronized,但可以使用synchronized代码块来进行同步

2.5:修饰一个静态方法,,代码如下

public synchronized static void method() {
   // todo
}

2.6:修饰一个类,代码如下:

class ClassName {
   public void method() {
      synchronized(ClassName.class) {
         // todo
      }
   }
}

总结:
1>:无论synchronized加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的是一个静态方法或一个类,则它取得的对象是该类,该类所有的对象是同一把锁;
2>:每个对象只有一个锁 [ lock ]与之相关联,谁拿到这个锁,谁就可以运行它所控制的那段代码;
3>:实现同步是需要很大的系统开销作为代价,甚至造成死锁,所以尽量避免无谓的同步控制。

你可能感兴趣的:(android开发之synchronized的用法)