Android 线程简单分析(一)
Android 并发之synchronized锁住的是代码还是对象(二)
Android 并发之CountDownLatch、CyclicBarrier的简单应用(三)
Android 并发HashMap和ConcurrentHashMap的简单应用(四)(待发布)
Android 并发之Lock、ReadWriteLock和Condition的简单应用(五)
Android 并发之CAS(原子操作)简单介绍(六)
Android 并发Kotlin协程的重要性(七)(待发布)
Android 并发之AsyncTask原理分析(八)(待发布)
Android 并发之Handler、Looper、MessageQueue和ThreadLocal消息机制原理分析(九)
Android 并发之HandlerThread和IntentService原理分析(十)
在java中多线程并发时,多个线程同时请求一个共享资源,必然会导致此资源的数据不安全,可能我们就需要synchronized同步避免线程并发访问共享资源导致数据不安全,但是synchronized锁住的是代码还是对象?
看一下栗子:
public class SynchronizedExample {
public synchronized void share() {
Log.e("tag", "share 开始执行.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("tag", "share 结束.....");
}
}
class SynchronizedExampleThread extends Thread {
@Override
public void run() {
SynchronizedExample example = new SynchronizedExample();
example.share();
}
}
1、在SynchronizedExample 的share方法加synchronized 打印结果:
share 开始执行.....
share 开始执行.....
share 开始执行.....
share 结束.....
share 结束.....
share 结束.....
可以看出来,上面的起动了三个线程,同时运行SynchronizedExample 类中的share方法,虽然share方法加上了synchronized,但是还是同时运行起来,好像synchronized并没起到同步的这用作用?
再来改改,改成同步代码块?
public void share() {
synchronized (this) {
Log.e("tag", "share 开始执行.....");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("tag", "share 结束.....");
}
}
结果还是和上面的一样
share 开始执行.....
share 开始执行.....
share 开始执行.....
share 结束.....
share 结束.....
share 结束.....
在java中,synchronized(this)以及非static的synchronized方法,只能防止多个线程同时执行同一个对象的同步代码段,即同一个锁监视器。
分析一下上面的代码?
实际上,在java中,对于非static的synchronized方法或者是synchronized代码块,锁的就是对象本身也就是this。是不是有答案了?
synchronized锁住的是括号里的对象,而不是代码。
当synchronized锁住一个对象后,别的线程如果也想拿到这个对象的锁,就必须等待(同步队列)这个线程执行完成释放锁,才能再次给对象加锁,这样才达到线程同步的目的,如果不明白可以看Android 线程简单分析(一)介绍,很详细。
所以我们在用synchronized关键字的时候,能缩小代码段的范围就尽量缩小,能在代码段上加同步就不要再整个方法上加同步。这叫减小锁的粒度,使代码更大程度的并发。锁的代码段太长了,别的线程是不是要等很久。
再看上面的代码,每个线程中都new了一个SynchronizedExample 类的对象,也就是产生了三个SynchronizedExample 对象,由于不是同一个对象,所以可以多线程同时运行synchronized方法或代码段。
验证:
SynchronizedExample example = new SynchronizedExample();
class SynchronizedExampleThread extends Thread {
@Override
public void run() {
example.share();
}
}
结果:
share 开始执行.....
share 结束.....
share 开始执行.....
share 结束.....
share 开始执行.....
share 结束.....
synchronized就起了作用。
为了在明确一点我在创建两个SynchronizedExample 对象:
SynchronizedExample example = new SynchronizedExample();
SynchronizedExample example2 = new SynchronizedExample();
class SynchronizedExampleThread extends Thread {
@Override
public void run() {
example.share();
}
}
class SynchronizedExampleThread2 extends Thread {
@Override
public void run() {
example2.share2();
}
}
结果:
share 开始执行.....
share2 开始执行.....
share 结束.....
share 开始执行.....
share2 结束.....
share2 开始执行.....
share 结束.....
share 开始执行.....
share2 结束.....
share2 开始执行.....
share 结束.....
share2 结束.....
明显同一个所得是同一个对象,即同一把锁。
未命名文件.png-
同一个对象每条线程进入同步代码块,其他线程将进入同步队列或锁池中等待其他线程释放锁,synchronize属于非公平锁,在锁池的等待的线程在线程1释放锁开始竞争锁,所以synchronize锁的是对象。
-
非公平锁和公平锁
1、非公平锁:线程加锁时直接尝试获取锁,获取不到就自动到队尾等待,ReentrantLock(默认)和synchronize属于非公平锁。
2、公平锁:加锁前先查看是否有排队等待的线程,有的话优先处理排在前面的线程,先来先得。 -
乐观锁和悲观锁
1、悲观锁:假设一定会发生并发冲突,通过阻塞其他所有线程来保证数据的完整性,就是属于悲观主义,synchronize就是悲观锁。
2、乐观锁:假设不会发生并发冲突,直接不加锁去完成某项更新,如果冲突就返回失败,CAS机制属于乐观的。
网友评论