这才是真正的synchronized

对于synchronized,想必是基础中的基础了,但是我们在使用时,还是会有一些模棱两可的地方,作为一个android程序员,对于java知识决不能只是够用即可,需要向深处挖掘,好了,在这里先要明确几个概念:

(1)synchronized大家都知道,他是一个锁机制,那么锁的是什么,这里要清楚,锁的是一个对象,而决不能把它想象成一个if语句。

(2)一个线程要想执行,首先满足的条件就是要有执行资格,这里的执行资格代表这个线程有权利去抢夺cpu资源,也就是一个正常被开启的线程,没有遇到wait,sleep或者join方法,而且有执行资格也不一定代表同一时间该线程一定会执行,因为还有可能没有抢到系统资源,而对于锁机制来说,一个线程即使有执行资格,也抢到了系统资源,但是没持有锁,该线程依然不会执行锁机制中的代码。

(3)对于synchronized而言,无论是声明在方法上,还是使用同步代码块,一旦多个线程面对的是同一把锁,也就是面对的锁对象是相同的,那么这个锁机制就会发生作用,出现的结果就是,同一时刻,只有获得该锁的线程,才有可能去执行声明synchronized中的代码,如果一个线程持有锁不释放,即使其他线程有执行资格也获取到了系统资源,对于声明synchronized中的代码依然不会执行。

(4)说到synchronized,不得不提几个方法,就是wait(),notify(),sleep(),yieId()

------wait():属于object的方法,wait使用的前提是该线程已经持有锁,在没有持有锁的情况下,线程里调用该方法,会抛出异常java.lang.IllegalMonitorStateException,也就是说wait()方法是依赖于锁存在的,一个正在运行的线程调用锁对象的wait方法,那么这个线程直接放弃执行资格,然后释放锁。

------notify():属于object的方法,它会随机去唤醒一个处于wait状态下的线程,notify只是一个唤醒的方法,它不保证一定会让唤醒的线程去运行,因为被notify唤醒的线程只是拥有了执行资格,但是如果此时锁没有被释放,那么该线程依然不会运行。简言之,就是notify只是停止了线程wait的状态,而只有此时锁没有被其他线程持有,该线程才有可能运行。

------sleep():属于线程的方法,一个线程调用sleep方法,会使该线程放弃执行资格,但是不会释放锁,也就是说,对于同一个锁对象的线程,一个线程sleep,那么其他线程即使有执行资格,也抢到了系统资源,由于锁依然被sleep的线程所持有,所以其他被锁机制控制的代码,依然不会执行。

------yield():线程让步的方法,一个线程调用该方法,会让所有优先级相同的线程,重新抢夺cpu资源,也就是说接下来所有的线程都有可能抢到cpu资源,包括调用yield的线程,但是要明确,调用该方法的的线程不会释放锁。

接下来我会用两个具有代表性的例子去说明上述的的情况,先看第一段代码

package com.example.hewenqiang.thread;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class ThreadActivity1 extends AppCompatActivity {
    private static final String FLAG = "hewenqiang";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread1);
        new Thread1().start();
        new Thread2().start();
        new Thread3().start();
    }

    class Thread1 extends Thread {
        @Override
        public void run() {
            synchronized (this) {
                Log.i(FLAG, "Thread1---start");
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.i(FLAG, "Thread1---end");
            }
        }
    }

    class Thread2 extends Thread {
        @Override
        public void run() {
            synchronized (this) {
                Log.i(FLAG, "Thread2---start");
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.i(FLAG, "Thread2---end");
            }
        }
    }

    class Thread3 extends Thread {
        @Override
        public void run() {
            synchronized (this) {
                Log.i(FLAG, "Thread3---start");
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.i(FLAG, "Thread3---end");
            }
        }
    }
}
这段代码打印的日志:

03-29 10:52:54.385 11528-12366/com.example.hewenqiang.thread I/hewenqiang: Thread1---start
03-29 10:52:54.385 11528-12367/com.example.hewenqiang.thread I/hewenqiang: Thread2---start
03-29 10:52:54.385 11528-12368/com.example.hewenqiang.thread I/hewenqiang: Thread3---start

03-29 10:52:57.385 11528-12366/com.example.hewenqiang.thread I/hewenqiang: Thread1---end
03-29 10:52:57.386 11528-12367/com.example.hewenqiang.thread I/hewenqiang: Thread2---end
03-29 10:52:57.386 11528-12368/com.example.hewenqiang.thread I/hewenqiang: Thread3---end

很显然,锁机制没有发挥任何作用,三个线程都进入了锁机制中的代码,那么关键的问题就在于,我们用的锁对象是this,也就是我们在创建线程时,是三个不同的对象,当然也就是三个不同的锁,所以这里并没有达到锁的效果,那么我们把锁对象换成同一个,再看代码

package com.example.hewenqiang.thread;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class ThreadActivity1 extends AppCompatActivity {
    private static final String FLAG = "hewenqiang";
    private Object object = new Object();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread1);
        new Thread1().start();
        new Thread2().start();
        new Thread3().start();
    }

    class Thread1 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                Log.i(FLAG, "Thread1---start");
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.i(FLAG, "Thread1---end");
            }
        }
    }

    class Thread2 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                Log.i(FLAG, "Thread2---start");
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.i(FLAG, "Thread2---end");
            }
        }
    }

    class Thread3 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                Log.i(FLAG, "Thread3---start");
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.i(FLAG, "Thread3---end");
            }
        }
    }
}
也很简单,就是换成了统一的锁对象,那么日志就会变为

03-29 11:00:48.135 18618-18664/com.example.hewenqiang.thread I/hewenqiang: Thread1---start

03-29 11:00:51.135 18618-18664/com.example.hewenqiang.thread I/hewenqiang: Thread1---end
03-29 11:00:51.136 18618-18665/com.example.hewenqiang.thread I/hewenqiang: Thread2---start
03-29 11:00:54.137 18618-18665/com.example.hewenqiang.thread I/hewenqiang: Thread2---end
03-29 11:00:54.137 18618-18666/com.example.hewenqiang.thread I/hewenqiang: Thread3---start
03-29 11:00:57.138 18618-18666/com.example.hewenqiang.thread I/hewenqiang: Thread3---end

所以我们验证了锁的是对象,而不是锁的是代码。

我们再来看第二组代码,这段代码很有代表性,这里可以充分的验证锁机制的特性

package com.example.hewenqiang.thread;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class ThreadActivity2 extends AppCompatActivity {
    private static final String FLAG = "hewenqiang";

    private Object object = new Object();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread2);
        new ThreadWait1().start();
        new ThreadWait2().start();
        new ThreadWait3().start();
        new ThreadWait4().start();
    }

    class ThreadWait1 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                try {
                    while (true) {
                        Log.i(FLAG, "ThreadWait1等待");
                        object.wait();
                        Log.i(FLAG, "ThreadWait1执行");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class ThreadWait2 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                try {
                    while (true) {
                        Log.i(FLAG, "ThreadWait2等待");
                        object.wait();
                        Log.i(FLAG, "ThreadWait2执行");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class ThreadWait3 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                try {
                    while (true) {
                        Log.i(FLAG, "ThreadWait3等待");
                        object.wait();
                        Log.i(FLAG, "ThreadWait3执行");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class ThreadWait4 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                while (true) {
                    Log.i(FLAG, "睡眠开始");
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    object.notify();
                    Log.i(FLAG, "睡眠时间到");
                }

            }
        }
    }
}
代码也很简单,花上几分钟,就可以看懂,关键是在ThreadWait4,在这个里边,我使用了一个无限循环,想要每次睡眠一秒唤醒一个线程,在这里我先不贴出Log日志,试想一下,会打印什么样的日志。很多朋友会想,ThreadWait1启动,那么会立马wait,然后释放锁,接着ThreadWait2启动,ThreadWait2也会立马wait然后释放锁,然后依次类推,(当然这只是其中一种情况,也可能ThreadWait1、ThreadWait2、ThreadWait3虽然启动了,但是可能还没有到wait就被ThreadWait4抢走系统资源了,这种情况先不讨论,我们还来讨论刚才的情况)紧接着ThreadWait4启动,那么由于该线程sleep时间到,紧接着notify,那么会有线程被唤醒,然后某个线程会执行,然后立马又等待,仔细看过我之前写的sleep方法的朋友会知道,sleep并不会释放锁,这时即使notify了,ThreadWait4还在循环,并没有释放锁,所以其他线程并不会执行。

那么,再细心分析代码,我们会发现,其实ThreadWait1、ThreadWait2、ThreadWait3中的线程已经进入锁机制控制的代码,就是说进入到锁里边了,而在ThreadWait4里又有唤醒的代码,即使ThreadWait4仍然持有锁,但是其他三个线程已经进入到锁里边了,按照代码执行的逻辑,这个时候应该不会再去判断锁,那么其他线程不就被唤醒了吗?显然,细心推敲,结合我之前所说,就不难理解,锁机制,不是if判断语句,我们不能说所谓的进入到锁里边,要非常明白,锁是锁的对象,不是一段代码,只要锁被其他线程持有,即使唤醒,即使该线程有执行资格,也有系统资源,由于获取不到锁,无论在锁机制中的代码的任何位置,都是无法继续执行,所以请看日志

03-29 11:13:25.389 30554-30631/com.example.hewenqiang.thread I/hewenqiang: ThreadWait1等待
03-29 11:13:25.390 30554-30632/com.example.hewenqiang.thread I/hewenqiang: ThreadWait2等待
03-29 11:13:25.390 30554-30633/com.example.hewenqiang.thread I/hewenqiang: ThreadWait3等待
03-29 11:13:25.391 30554-30634/com.example.hewenqiang.thread I/hewenqiang: 睡眠开始

03-29 11:13:26.391 30554-30634/com.example.hewenqiang.thread I/hewenqiang: 睡眠时间到
03-29 11:13:26.391 30554-30634/com.example.hewenqiang.thread I/hewenqiang: 睡眠开始
03-29 11:13:27.392 30554-30634/com.example.hewenqiang.thread I/hewenqiang: 睡眠时间到
03-29 11:13:27.392 30554-30634/com.example.hewenqiang.thread I/hewenqiang: 睡眠开始
03-29 11:13:28.392 30554-30634/com.example.hewenqiang.thread I/hewenqiang: 睡眠时间到
03-29 11:13:28.392 30554-30634/com.example.hewenqiang.thread I/hewenqiang: 睡眠开始
03-29 11:13:29.392 30554-30634/com.example.hewenqiang.thread I/hewenqiang: 睡眠时间到
...............

就这么一直打印下去,原因就是ThreadWait4一直持有锁,如果想让其他三个线程唤醒,也很简单,不要无限循环,来一个for循环,然后循环结束,释放锁,那么其他线程就会可能去执行,请看代码

package com.example.hewenqiang.thread;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class ThreadActivity2 extends AppCompatActivity {
    private static final String FLAG = "hewenqiang";

    private Object object = new Object();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread2);
        new ThreadWait1().start();
        new ThreadWait2().start();
        new ThreadWait3().start();
        new ThreadWait4().start();
    }

    class ThreadWait1 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                try {
                    while (true) {
                        Log.i(FLAG, "ThreadWait1等待");
                        object.wait();
                        Log.i(FLAG, "ThreadWait1执行");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class ThreadWait2 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                try {
                    while (true) {
                        Log.i(FLAG, "ThreadWait2等待");
                        object.wait();
                        Log.i(FLAG, "ThreadWait2执行");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class ThreadWait3 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                try {
                    while (true) {
                        Log.i(FLAG, "ThreadWait3等待");
                        object.wait();
                        Log.i(FLAG, "ThreadWait3执行");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class ThreadWait4 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                for (int a = 0; a < 3; a++) {
                    Log.i(FLAG, "睡眠开始");
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    object.notify();
                    Log.i(FLAG, "睡眠时间到");
                }

            }
        }
    }
}
然后看日志:

03-29 11:42:25.308 510-600/com.example.hewenqiang.thread I/hewenqiang: ThreadWait1等待
03-29 11:42:25.314 510-602/com.example.hewenqiang.thread I/hewenqiang: ThreadWait2等待
03-29 11:42:25.315 510-603/com.example.hewenqiang.thread I/hewenqiang: ThreadWait3等待
03-29 11:42:25.316 510-604/com.example.hewenqiang.thread I/hewenqiang: 睡眠开始

03-29 11:42:26.317 510-604/com.example.hewenqiang.thread I/hewenqiang: 睡眠时间到
03-29 11:42:26.317 510-604/com.example.hewenqiang.thread I/hewenqiang: 睡眠开始
03-29 11:42:27.317 510-604/com.example.hewenqiang.thread I/hewenqiang: 睡眠时间到
03-29 11:42:27.317 510-604/com.example.hewenqiang.thread I/hewenqiang: 睡眠开始
03-29 11:42:28.317 510-604/com.example.hewenqiang.thread I/hewenqiang: 睡眠时间到
03-29 11:42:28.318 510-600/com.example.hewenqiang.thread I/hewenqiang: ThreadWait1执行
03-29 11:42:28.318 510-600/com.example.hewenqiang.thread I/hewenqiang: ThreadWait1等待
03-29 11:42:28.318 510-602/com.example.hewenqiang.thread I/hewenqiang: ThreadWait2执行
03-29 11:42:28.318 510-602/com.example.hewenqiang.thread I/hewenqiang: ThreadWait2等待
03-29 11:42:28.318 510-603/com.example.hewenqiang.thread I/hewenqiang: ThreadWait3执行
03-29 11:42:28.318 510-603/com.example.hewenqiang.thread I/hewenqiang: ThreadWait3等待

说到这里,想必大家已经很清楚,我这篇文章在说什么,也就是synchronized锁的是对象,对于同一个锁对象的线程,只要某一个线程持有锁,其他线程锁中的代码都不会去执行。





你可能感兴趣的:(这才是真正的synchronized)