本内容整理自:博客园-海 子-java并发编程系列-http://www.cnblogs.com/dolphin0520/category/602384.html

1、基础:

  1、什么时候出现线程安全问题?

    在多线程编程中,可能会出现多个线程同时访问一个资源(共享资源)的情况,由于每个线程的过程不可控,导致程序实际运行结果与愿望相违背。

  2、如何解决线程安全问题?

    一般而言,所有的并发模式都采用“序列化访问临界资源”的方案:即在同一时刻,只允许一个线程访问临界资源。

    上述方式也称为:同步互斥访问:在访问临界资源的代码前加一个所,访问完以后释放锁。java中有两种方式实现互斥同步:synchronized和Lock;

  3、线程创建的两种方式:来自 博客园-海 子-http://www.cnblogs.com/dolphin0520/p/3913517.html

2、synchronized实现方式

  1、synchronized加锁的三种方式及对应原理:

    (1):synchronized修饰普通方法,锁范围是实例范围:所有的对象都自动含有单一的锁(监视器monitor),调用对象中的synchronized方法时,该对象会被加锁,该对象内的其他加锁方法不能被其他线程访问。如:  Person(){synchronized eat(){} synchronized run()};当线程thread1调用该对象的eat时,thread2不能调用run方法;

      底层原理:如:以常量池中ACC_SYNCHRONIZED标示符,为访问标识,方法调用时,先检查该访问标志是否被设置,若是,则线程先获取monitor,方法执行完释放monitor。

public synchronized void method(){
System.out.println("Hello Word") ;
}

    (2):synchronized修饰static方法,锁范围是类范围:所有的Person类对象都会被锁住。线程1和线程2的eat1方法只能被一个调用。而eat2方法是实例同步的,线程1锁住对象1,线程2锁住对象2,eat2可以被线程1和线程2同步调用。

public class Person implements Runnable{
static int apple=0;
//锁住当前的Class类对象,即Person类。
public static synchronized void eat1(){
i++;//非原子性操作;
}
    //锁住当前实例,即只对Person的一个实例对象加锁;
public synchronized void eat2(){
        i++;
   }
}

    (3): synchronized修饰同步代码块,所范围是实例对象:有时对整个方法进行加锁,显得不必要,同时也会降低执行效率,需要的仅仅是对方法中某几步操作进行加锁。

public class Person{
private int apple=0;
public void eat(){
synchronized(this){
apple++;
}
}
}

  

注:一个线程可以获取多次该对象的锁:如对象o1调用eat方法,而eat又调用了该对象的run方法,jvm会自动递增加锁的次数,每次离开一个synchronized方法,计数减一,一直到0。

注:synchronized的优化等内容见以后整理(待补充。。。)。

3、Local的实现方式(java.util.concurrent.locks包下Local接口)

  1、有了synchronized为什么还要使用Local?

    (1)synchronized的缺陷:synchronized只有两种执行结果[1]执行完释放锁。[2]异常,jvm释放锁。但是当一个持有锁的线程被阻塞(如IO),那么其他线程也只能等。再比如,多线程同时读取文件时,不会造成冲突,但是为了避免读写冲突,读操作也被synchronized,效率很低。

    (2)使用Local能解决上述问题,而且,Local是java中的一个类(接口),实现多种方法来获取处理锁;synchronized是java语言内置关键字,无别功能。

  2、Local的使用方式:

  (1):获取锁的方式:

Lock中有四种获取锁的方法:    

(1):lock():获取锁,若锁被其他线程获取,等待。常用try&catch包围,用完后需要主动释放,unlock();
(2):tryLock():尝试获取锁,锁为空闲状态才获取该锁,若成功返回true;失败返回false;
(3):tryLock(long time, TimeUnit unit):尝试获取锁,并在等待时间内尝试获取,,获取到返回true;
(4):lockInterruptibly():使用该方法获取锁,若正在等待获取,改线程可以相应interrupt()方法,结束等待。

  (2):方法内声明并lock与方法外声明,方法内lock:

public class Test {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
public static void main(String[] args) {
final Test test = new Test(); new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start(); new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
} public void insert(Thread thread) {
Lock lock = new ReentrantLock();//Lock在方法内,两个线程两次new
lock.lock();
try {
System.out.println(thread.getName()+"得到了锁");
for(int i=0;i<5;i++) {
arrayList.add(i);
}
} catch (Exception e) {
// TODO: handle exception
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock();
}
}
}
Thread-0得到了锁
Thread-1得到了锁
Thread-0释放了锁
Thread-1释放了锁
//线程0获取锁不影响线程1的获取
 
public class Test {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
private Lock lock = new ReentrantLock(); //注意这个地方
public static void main(String[] args) {
final Test test = new Test(); new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start(); new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
} public void insert(Thread thread) {
if(lock.tryLock()) {
try {
System.out.println(thread.getName()+"得到了锁");
for(int i=0;i<5;i++) {
arrayList.add(i);
}
} catch (Exception e) {
// TODO: handle exception
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock();
}
} else {
System.out.println(thread.getName()+"获取锁失败");
}
}
}

Thread-0得到了锁
Thread-1得到了锁
Thread-1释放了锁
Thread-0释放了锁

 

  3、ReentrantLock:是接口Lock的唯一直接实现类,一般使用 Lock lock = new ReentrantLock(); 来获取一个锁对象。

4、锁的几种类型

  1、可重入锁:thread1执行锁方法method1,metho1调用锁方法method2时不必重新获取锁(同一对象);synchronized与reentrantLock都是可重入锁,

  2、可中断锁:synchronized不可中断,Lock是可以中断的(如lockInterruptibly())

  3、公平锁:尽量以请求锁的顺序来获取锁。synchronized不公平锁,ReentrantLock默认不公平,可以设为公平。

  4、读写锁:读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。可以通过readLock()获取读锁,通过writeLock()获取写锁。

  具体事例参见大神ref:博客园-海 子-http://www.cnblogs.com/dolphin0520/p/3923167.html

5、Lock和synchronized的比较

(1):Lock是一个接口,主要实现类有:ReentrantLock(直接实现类)和ReentrantReadWriteLock。synchronized是Java中的关键字,是内置的语言实现。
(2):synchronized异常时会自动释放锁,Lock必须主动释放(必须放在finally{*.unlock()});
(3):Lock可以实现让等待锁的线程相应中断(interrupt);synchronized不可以。
(4):Lock可以获知有没有获取到锁。
(5):Lock可以提高多个线程进行『读』操作的效率。资源竞争不激烈,两者差不多;资源竞争激烈,Lock性能远好于synchronized。

更详细请移步:博客园-海 子-java并发编程系列-http://www.cnblogs.com/dolphin0520/category/602384.html    

        

  

    

  

最新文章

  1. Error Domain=NSURLErrorDomain Code=-999 “The operation couldn’t be completed.
  2. webservice返回值为Map类型的处理方法
  3. 开发工程师面试的秘密( 整理自 Export C Programming )
  4. nginx限制访问速度
  5. 【poj2891-Strange Way to Express Integers】拓展欧几里得-同余方程组
  6. eclipse中DDMS的LOGcat只有一列level
  7. hdu 4841 圆桌问题(STL vector)
  8. powershell 将文本转换成表格的还有一种方式
  9. 解决Windows 7下IE11无法卸载、无法重新安装,提示安装了更新的IE版本
  10. 通过memcached来实现对tomcat集群中Session的共享策略
  11. std::vector 源代码
  12. .NET Core 中的通用主机和后台服务
  13. linux文件压缩解压命令
  14. Solidity合约间的调用-1
  15. Scala进阶之路-Spark独立模式(Standalone)集群部署
  16. JdbcTemplate中向in语句传参
  17. Caffe + Ubuntu 14.04 64bit + CUDA 6.5 配置说明2
  18. node.js 笔记一
  19. 分布式系统的那些事儿(六) - SOA架构体系
  20. 反编译apk 修改 合成

热门文章

  1. MyBatis分页插件PageHelper使用方法
  2. kali Linux openvas忘记密码的解决方法
  3. 【C# IO 操作】使用StringWriter和StringReader的好处
  4. WPF中使用MVVM进行multibinding
  5. js求质数(1-100)
  6. oj教程--学习顺序
  7. 针对python输入要求
  8. JavaSE高级编程之多线程
  9. WPS:在Word中插入Visio绘制的流程图
  10. c/c++ 日常积累