本内容整理自:博客园-海 子-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    

        

  

    

  

Java并发机制(2)--synchronized与Lock的更多相关文章

  1. Java并发编程:synchronized、Lock、ReentrantLock以及ReadWriteLock的那些事儿

    目录 前言 synchronized用法 修饰方法 修饰实例方法 修饰静态方法 同步代码块 引出Lock Lock用法 子类:ReentrantLock 读写分离锁:ReadWriteLock Loc ...

  2. Java并发编程:synchronized和Lock

    转自  :   http://www.tuicool.com/articles/qYFzUjf

  3. Java并发编程:synchronized

    Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...

  4. 《Java并发编程的艺术》Java并发机制的底层实现原理(二)

    Java并发机制的底层实现原理 1.volatile volatile相当于轻量级的synchronized,在并发编程中保证数据的可见性,使用 valotile 修饰的变量,其内存模型会增加一个 L ...

  5. 4、Java并发编程:synchronized

    Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...

  6. 【java并发编程艺术学习】(三)第二章 java并发机制的底层实现原理 学习记录(一) volatile

    章节介绍 这一章节主要学习java并发机制的底层实现原理.主要学习volatile.synchronized和原子操作的实现原理.Java中的大部分容器和框架都依赖于此. Java代码 ==经过编译= ...

  7. Java 并发系列之二:java 并发机制的底层实现原理

    1. 处理器实现原子操作 2. volatile /** 补充: 主要作用:内存可见性,是变量在多个线程中可见,修饰变量,解决一写多读的问题. 轻量级的synchronized,不会造成阻塞.性能比s ...

  8. Java并发机制的底层实现原理之volatile应用,初学者误看!

    volatile的介绍: Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现 ...

  9. Java 并发机制底层实现 —— volatile 原理、synchronize 锁优化机制

    本书部分摘自<Java 并发编程的艺术> 概述 相信大家都很熟悉如何使用 Java 编写处理并发的代码,也知道 Java 代码在编译后变成 Class 字节码,字节码被类加载器加载到 JV ...

  10. 《Java并发编程的艺术》读书笔记:二、Java并发机制的底层实现原理

    二.Java并发机制底层实现原理 这里是我的<Java并发编程的艺术>读书笔记的第二篇,对前文有兴趣的朋友可以去这里看第一篇:一.并发编程的目的与挑战 有兴趣讨论的朋友可以给我留言! 1. ...

随机推荐

  1. SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分库分表实践

    一.序言 在实际业务中,单表数据增长较快,很容易达到数据瓶颈,比如单表百万级别数据量.当数据量继续增长时,数据的查询性能即使有索引的帮助下也不尽如意,这时可以引入数据分库分表技术. 本文将基于Spri ...

  2. 三大流行BI分析平台推荐,企业数据化选择工具

    进入大数据时代以来,对于企业来说,海量的数据不仅是财富,也是负担.无论是大型企业还是小型企业,都面临着同样的挑战--如何利用大数据客户体验,有效达到优化生产力的效果.这也是近年来许多企业选择搭建现代大 ...

  3. 【C#】COM线程模型-套间 ApartmentState

    线程模式是微软的COM基础中的极其重要的概念.一定要吃透!初始一个STA套间实际上是相当于开了一个消息窗口,所有调用经此窗口过程调度到组件内. [STAThread] 可以理解成CoInitializ ...

  4. Map<String, String> 遍历的四种方法

    Map<String, String> map = new HashMap<String, String>(); map.put("key1", " ...

  5. linux多进/线程编程(2)—— fork函数和进程间“共享”数据

    参考: 1.博客1:https://www.pianshen.com/article/4305691855/ fork:在原进程的基础上"分叉"出一个子进程,即创建一个子进程. N ...

  6. iNeuOS工业互联网操作系统下发命令给iNeuLink硬件网关,进一步修改设备参数和控制设备

    目       录 1.      应用场景... 1 2.      DCS数据采集... 2 3.      硬件网关的配置... 2 4.      平台端配置... 3 1.   应用场景 i ...

  7. Java变量,作用域,常量

    JAVA变量,作用域,常量 变量 变量是什么:就是可以变化的量! Java是一种强类型的语言,每变量都必须声明其类型. Java变量是程序中最基本的存储单元,其要素包括:变量名,变量类型和作用域 格式 ...

  8. JZ-050-数组中重复的数字

    数组中重复的数字 题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中 第一个重复的数字. 例 ...

  9. JZ-035-数组中的逆序对

    数组中的逆序对 题目描述 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数P.并将P对1000000007取模的结果输出. 即 ...

  10. Python 细聊可以媲美 PS 的 PIL 图片处理库

    1 . 前言 PIL 是 Python Image Library 的简称. PIL 库中提供了诸多用来处理图片的模块,可以对图片做类似于 PS(Photoshop) 的编辑.比如:改变图像大小.旋转 ...