线程、volatile与synchronized、Lock
线程
1、概念:
进程是程序运行资源分配的最小单位。
线程是CPU调度的最小单位,必须依赖于进程而存在。
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的、能独立运行的基本单位。
2、线程生命周期:
线程生命周期的5种状态:
Java线程具有五中基本状态
新建状态(New):当线程对象被创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();),线程即进入就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,并不是说执行了t.start()此线程立即就会执行;
运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中;
阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待队列中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
结束状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
Tip:
当调用线程的yield()方法时,线程从运行状态转换为就绪状态,但接下来CPU调度就绪状态中的哪个线程具有一定的随机性,因此,可能会出现A线程调用了yield()方法后,接下来CPU仍然调度了A线程的情况。
3、线程调度
共同点:
- wait()和sleep()他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。
- wait()和sleep()都可以通过interrupt()方法 打断线程的暂停状态
不同点 - Thread类的方法:sleep(),yield()等
Object对象的方法:wait()和notify()等 - 每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。
sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法 - wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
- sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
4、线程实现
4.1、实现方式
继承Thread覆盖run()方法。
实现Runnable接口,实现run()方法。
实现Callable接口,实现call()方法。
4.2、之间的区别:
4.2.1、Thread类与Runnable接口区别:
Thread:每个线程都独立,不共享资源。
Runnable:存在线程共享概念。
4.2.2、Runnable和Callable的区别:
两者最大的不同点是:
1)是否能返回执行结果:
<font color=red>实现Callable接口的任务线程**能返回执行结果**;而实现Runnable接口的任务线程**不能返回结果**</font>;
2)方法是否能抛出异常:
Callable接口的<font color=red>call()方法**允许抛出异常**</font>;而Runnable接口的<font color=red>run()方法的异常只能在内部消化,**不能继续上抛**</font>;
5、线程安全
线程安全性包括两方面:可见性和原子性。
5.1、volatile与synchronized
5.1.1、volatile
volatile关键字解决的是内存可见性的问题:
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
使用volatile修饰的变量会强制将修改的值立即写入主存,主存中值的更新会使缓存中的值失效。这时候会去主内存读取。
5.1.2、volatile与synchronized区别
1) volatile
轻量级,只能修饰变量。synchronized
重量级,可以修饰变量、方法、类。
2) volatile
只能保证数据的内存可见性,不能保证原子性,因为多个线程并发访问volatile修饰的变量不会阻塞。
synchronized
不仅保证内存可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。
一个线程执行临界区代码过程如下:
1 获得同步锁
2 清空工作内存
3 从主存拷贝变量副本到工作内存
4 对这些变量计算
5 将变量从工作内存写回到主存
6 释放锁
所以说:仅仅使用volatile并不能保证线程安全性。而synchronized则可实现线程的安全性。
5.1、synchronized关键字的使用和缺点
1)、获取锁的线程执行完毕,然后线程释放对锁的占有
2)、线程执行发生异常,此时JVM会让线程自动释放锁
缺点:
即使大量读操作也会堵塞,不会并发去读。如果线程中有堵塞超时任务,其他任务也不会往下执行,全部堵塞了。
5.2、Lock
5.2.1、Lock接口中常用方法
lock()
tryLock() :尝试获取锁
tryLock(long time, TimeUnit unit):按照超时时间获取锁
lockInterruptibly():打断锁(注意,当一个线程获取了锁之后,是不会被interrupt()方法中断的)
unLock():释放锁
5.2.2、lock线程间的通信:
Conditon中的await()对应Object的wait();
Condition中的signal()对应Object的notify();
Condition中的signalAll()对应Object的notifyAll()
5.2.3、ReentrantLock(可重入锁)
ReentrantLock支持两种获取锁的方式,一种是公平模型,一种是非公平模型。
公平锁:保证执行顺序,先进先出,FIFO
非公平锁:JVM优化后一种插队模式,后进先出,LIFO(先进的数据可能一直消费不到)
5.2.4、ReentrantReadWriteLock (读写锁)
允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。
相对于排他锁,提高了并发性。
在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。
5.3、lock和synchronized区别:
Lock和synchronized的选择:
类别 | synchronized | Lock |
---|---|---|
存在层次 | Java的关键字,在jvm层面上 | 是一个接口 |
锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 |
在finally中必须释放锁,不然容易造成线程死锁 |
锁的获取 | 假设A线程获得锁,B线程等待。 如果A线程阻塞,B线程会一直等待 |
分情况而定,Lock有多个锁获取的方式,可以尝试获得锁,线程可以不用一直等待 |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入 不可中断 非公平 |
可重入 可判断 可公平(两者皆可) |
性能 | 少量同步 | 大量同步 |
Lock可以提高多个线程进行读操作的效率。
6、守护线程
守护线程,可以理解为后台运行线程。进程结束,守护线程自然而然地就会结束,不需要手动的去关心和通知其状态。Java的垃圾回收也是一个守护线程。
它的好处就是你不需要关心它的结束问题。
线程、volatile与synchronized、Lock的更多相关文章
- Java 线程锁机制 -Synchronized Lock 互斥锁 读写锁
(1)synchronized 是互斥锁: (2)ReentrantLock 顾名思义 :可重入锁 (3)ReadWriteLock :读写锁 读写锁特点: a)多个读者可以同时进行读b)写者必须互斥 ...
- 剑指Offer——线程同步volatile与synchronized详解
(转)Java面试--线程同步volatile与synchronized详解 0. 前言 面试时很可能遇到这样一个问题:使用volatile修饰int型变量i,多个线程同时进行i++操作,这样可以实现 ...
- 线程同步的实现方式(volatile、synchronized、CountDownLatch)
题目: 自定义容器,提供新增元素(add)和获取元素数量(size)方法.启动两个线程. 线程1向容器中新增10个数据.线程2监听容器元素数量,当容器元素数量为5时,线程2输出信息并终止. 方法一:v ...
- 线程同步Volatile与Synchronized(一)
volatile 一.volatile修饰的变量具有内存可见性 volatile是变量修饰符,其修饰的变量具有内存可见性. 可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改 ...
- Java并发——线程同步Volatile与Synchronized详解
0. 前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52370068 面试时很可能遇到这样一个问题:使用volatile修饰in ...
- JMM内存模型+volatile+synchronized+lock
硬件内存模型: Java内存模型: 每个线程都有一个工作内存,线程只可以修改自己工作内存中的数据,然后再同步回主内存,主内存由多个内存共享. 下面 8 个操作都是原子的,不可再分的: 1) lock ...
- volatile、synchronized、lock有什么区别,以及在哪些场景下使用哪种方式?
[转]JVM锁机制volatile/synchronized/lock 1.volatile实现原理 (1)聊聊并发(一)——深入分析Volatile的实现原理 --硬件级别锁实现,Lock前缀指令会 ...
- synchronized (lock) 买票demo 线程安全
加锁防止多个线程执行同一段代码! /** http://blog.51cto.com/wyait/1916898 * @author * @since 11/10/2018 * 某电影院目前正在上映贺 ...
- 线程池 队列 synchronized
线程池 BlockingQueue synchronized volatile 本章从线程池到阻塞队列BlockingQueue.从BlockingQueue到synchronized 和 volat ...
随机推荐
- JAVA 算法练习(一)
用java写了几道编程题目,分享给大家 语法和C语言几乎一样,不懂 java 会 c 也可以看明白的. 最大连续数列和 题目说明 对于一个有正有负的整数数组,请找出总和最大的连续数列.给定一个int数 ...
- PowerDesigner 表格导出为excel(转载)
选中tablesctrl + shift +x 然后运行脚本 '******************************************************************** ...
- Python笔记_第四篇_高阶编程_二次封装
1.二次封装: 二次封装其实就是对一个类或者一个方法进行二次的改造增加新的功能. 2.一个类的二次封装: 我们以一个进程为例,我们把Process这个库进行二次封装,增加一些功能,在调用. thoma ...
- 静态页面缓存(thymeleaf模板writer)
//前端html <!DOCTYPE html><html lang="en"> <head> <meta charset="U ...
- 吴裕雄--天生自然 PHP开发学习:MySQL子句
<?php $con=mysqli_connect("localhost","username","password","d ...
- C++如何输入含空格的字符串
1.scanf函数(包含头文件#include <stdio.h>) scanf函数一般格式为scanf(“%s”,st),但scanf默认回车和空格是输入不同组之间的间隔和结束符号,所以 ...
- Python 学习笔记:Python 连接 SQL Server 报错(20009, b'DB-Lib error message 20009, severity 9)
问题及场景: 最近需要使用 Python 将数据写到 SQL Server 数据库,但是在进行数据库连接操作时却报以下错误:(20009, b'DB-Lib error message 20009, ...
- rabbitmq安装(linux)遇到 很多坑
1.下载erlang官网地址 http://www.erlang.org/download 挑选合适的版本 然后 建议20.3运行命令 wget http://erlang.org/download/ ...
- 吴裕雄--天生自然 PYTHON3开发学习:函数
def 函数名(参数列表): 函数体 # 计算面积函数 def area(width, height): return width * height def print_welcome(name): ...
- ansible批量部署模块(二)
回顾:Ansible: 无需客户端程序 只要有SSH 模块化 ansible帮助工具ansible-doc 模块名ansible-doc 模块名 -s 列出该模块的所有选项ansible-doc -l ...