0%

Java语言synchronized关键字自带了一个内置的隐性锁(implicit lock),使用起来方便简单,但是内置锁一旦使用,则会强制将某个代码块加锁或者解锁,而且内置锁并不支持可中断的获取锁。从Java5.0开始,提供了一个并发工具包java.util.concurrent.*,实现了显性锁(explicit lock)ReentrantLock(可重入锁,可多次获取同一个锁);ReentrantLock实现了与synchronized一样的功能,确保并发过程中数据的互斥访问与可见性。获取ReentrantLock相当于进入一个synchronized代码块,而释放ReentrantLock则相当于从一个synchronized代码块退出。ReentrantLock实现了如下Lock.java接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

public interface Lock {
//获取锁
void lock();

//获取锁,可响应中断
void lockInterruptibly() throws InterruptedException;

//尝试获取锁,如果可用则返回TRUE,否则返回FALSE
boolean tryLock();

//尝试在给定时间内获取锁,如果超时或者发生中断,则返回FALSE
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

//释放锁
void unlock();

//创建与该锁绑定的Conidition变量
Condition newCondition();
}


阅读全文 »

在古希腊时代,哲学家Democritus(德谟克利特)提出了关于物质构成的理论:所有物质都由不可分割的元素组成,这种元素被称之为原子(atom)。编程中所说的原子性(atomic)借用了这个概念, 用于表示某个代码指令动作的不可中断性。在之前的一篇文章(Java Memory Model)时,提到多线程并发访问共享数据时,会出现数据竞争,从而导致数据不一致的情况。比如对一个整型变量进行加一的操作: ++counter,表面上看,这是一个单一原子操作,但实际上这个操作有三个步骤:首先,需要从内存中(有可能是cache中)加载到寄存器;接着,将该值加一;最后需要将寄存器中的值写入内存。这样,在多个线程访问的情况,上述几个步骤出现交织执行,就可能出现各个线程读写数据不一致的情形。

Atomic Theory: http://www.softschools.com/timelines/atomic_theory_timeline/95/

于是,有了原子操作。原子操作确保了执行的不可中断,因而能避免数据冲突。Java从5.0开始有一个atomic的工具包专门支持intlong以及引用变量的原子操作,而在硬件层面,目前大部分处理器都支持诸如CAS(CompareAndSet/CompareAndSwap),FAA(FetchAndAdd)等原子指令。在介绍Java中的原子操作类之前,先来了解下硬件层面的原子指令。

阅读全文 »

在之前的一篇文章(Glide架构分析 )中介绍了Glide的具体原理。这篇文章,用一个下载图片的示例来说明Glide加载图片的整个过程。以下是用Glide从网络上加载一个图片的代码片段:

1
2
3
4
5
6
7
8
9
    
Glide
.with(activity) // Activity
.load(url)
.centerCrop()
.placeholder(R.drawable.loading_spinner)
.crossFade()
.into(myImageView);

大致说来,在Glide中图片的加载有如下几个过程:

  • 产生一个图片加载的请求GenericRequest<T>;
  • 将图片加载请求发送给资源引擎中心Engine,由其负责资源的加载以及数据解码任务的管理;
  • EngineRunnable首先尝试从DISK加载资源,完成后进行回调,将数据加载到ImageView中;
  • 磁盘中没有请求对应的资源,则尝试从网络端下载,并解码完成后回调,将数据加载到Target对象;

现在就来详细的看下Glide加载图片的整个过程。

阅读全文 »

在之前一篇文章里,讲到了利用synchronized关键字来进行同步,从而避免多线程并发执行时可能出现的竞争条件,那么JVM又是如何实现线程之间的通信的?换句话说,线程A写共享数据的结果怎么确保被其他线程可见,使得线程之间共享的数据对每个线程而言都是一致的?Java从5.0开始定义了一个新的Memory Model(Java Specification Request 133, JSR133),在多线程情况下,为Java程序提供了一个最少程度的保证:正确同步的情况下,一个线程写共享变量对于其他线程是可见的。Java Memory Model(JMM)抽象了不同计算机平台底层内存读写细节(Register; Cache; Main Memory),为程序员提供了一个访问内存的统一的模型与视角,从而确保在不同平台上程序能有同样的结果,实现Java“编写一次,可以在任何平台上执行”的目标。

阅读全文 »

与Java程序类似,Android应用程序框架层(Application Framework)以及APP运行在一个Dalvik Virtual Machine之上,那么,Android启动时框架层是如何初始化的,从何处初始化的?为此,Android在启动时会首先初始化一个专门的系统进程zygote来负责启动与初始化Java代码,比如系统服务进程system_server的启动,系统启动时各种Java服务的初始化,APP资源文件的加载,APP进程的创建与启动。这篇文章,主要讲zygote进程的两个问题:

  • zygote进程如何初始化的;
  • APP进程是如何通过zygote创建的?
阅读全文 »

在计算机诞生初期,由于其高昂的计算成本,只能允许一个用户运行一个任务或者一批任务(Batch processing)。随着技术的发展,人们开始思考一个问题,如何在计算机上实现多个任务“同时”运行?一开始,采用的是分时策略(Time sharing),就是允许多个用户共享一台计算机,但每个用户占用计算机的一个时间片,在这个时间后,由另一个用户接着运行使用。分时系统在一定程度上提高了计算效率,实现了资源的共享。但其实际上并没有真正实现任务的“同时”(并发,concurrency)运行。

现代并发编程(Concurrent Pragramming)概念的出现一方面是受到操作系统中如进程、中断以及抢占的影响;一方面由于计算机硬件技术的发展而出现的多核处理器。

  • 进程,是程序执行的一个实体,是操作系统对CPU,寄存器,堆栈,内存,文件系统等计算资源的一种执行时的抽象;有了进程,计算机就可以通过调度系统来实现多个任务“同时”运行了,这里所谓的“同时”并不是多个程序真的在一个CPU中同时运行,而是说调度程序快速的在多个进程之间切换,交替执行不同程序,从而更有效的利用了计算机资源;
  • 多核CPU的出现,为并发、并行计算提供了另一种可能。以前程序只能在一个CPU上运行,现在单个程序可以同时在多个CPU上同时运行了(Parallel Computing);或者多个进程同时在多个CPU上运行。
阅读全文 »

Glide是BumpTech开发的一个同时可支持图片、GIF以及视频加载的Android开源库,同时Glide支持任何用户自定义的网络栈,其主要有以下几个特点:

  • 支持GIF格式动画的解码;
  • 支持本地视频加载解码;
  • 加载大图之前,可预先加载一个小图片
  • 自动管理资源加载请求的生命周期;
  • 给定图片尺寸,可以对资源进行自由转换

Github:https://github.com/bumptech/glide

这里就来分析下Glide的具体架构以及介绍其主要特点,分如下几个方面:

  • Glide代码架构及工作原理;
  • Glide如何自动管理资源加载请求的生命周期的?
  • Glide是如何进行图片的加载与转换的?
阅读全文 »

在单一线程执行的情况下,并不用考虑任何数据一致性与同步等问题,但到了多个线程执行的情况下,共享数据的同步就显得至关重要了。比如有一个银行账户的操作问题(例子来自Wikipedia),现在有两个线程A与B共享一个账户变量balance,这个提款的操作有两个部分,首先需要判定提款数目是否小于当前账户存款,记为s1;如果该条件满足,则从账户中提取资金,记为s2。假定开始时账户balance=600,现在A调用withdraw(200),B调用withdraw(500),如果两个线程A与B调用时,s1都发生在s2之前,则两个线程都可以进入判定条件,提取相应的资金,而实际的存款是小于两个线程需要提取的资金的。

1
2
3
4
5
6
7
8
9
10
11
12
13

bool withdraw(int withdrawal)
{

if (balance >= withdrawal) // --> s1
{

balance -= withdrawal; // --> s2
return true;
}
return false;
}

这种线程之间共享资源的一致性同步问题在并发编程中十分常见,通常被称为Data Race。根据官网上的定义(what is data race),Data Race出现是由以下原因导致:

  • 多个线程同时访问共享内存;
  • 至少有一个线程写该共享内存区域;
  • 线程访问共享内存并没有利用锁进行同步;
阅读全文 »

线程作为操作系统的最小调度单位。一个进程里可以有多个线程,这些线程有各自的程序计数器、堆栈空间和局部变量,而且可以共享进程的内存空间,因而在上下文切换时时间更短,效率更高,也常被成为轻量级进程(Light Weight Process)。接下来,将从三个方面来介绍Java线程类Thread的具体用法:

  • 线程构造与初始化;
  • 线程状态切换;
  • 线程变量ThreadLocal的使用

Thread: https://en.wikipedia.org/wiki/Thread_(computing)

阅读全文 »