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>:实现同步是需要很大的系统开销作为代价,甚至造成死锁,所以尽量避免无谓的同步控制。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种: 修饰一个代码块,被修饰的代码...
    明教de教主阅读 666评论 0 2
  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,690评论 0 11
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,438评论 1 15
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,949评论 1 18
  • 我多么希望,有一个门口 早晨,阳光照在草上 我们站着 扶着自己的门扇 门很低,但太阳是明亮的 草在结它的种子 风在...
    如是一一阅读 293评论 5 1