java多线程-synchronized
一、线程安全问题
多线程操作各自线程创建的资源的时候,不存在线程安全问题。但多线程操作同一个资源的时候就会出现线程安全问题。下例为两个线程操作同一个name资源时发生的问题。
- class TestSyn {
- public static void main(String[] args) throws Exception {
- Resource resource = new Resource();
- new Thread() {
- @Override
- public void run() {
- while (true) {
- r.sayb();
- }
- }
- }.start();
- new Thread() {
- @Override
- public void run() {
- while (true) {
- r.sayq();
- }
- }
- }.start();
- }
- }
- class Resource {
- private String name;
- public void sayb() {
- name = "bbb";
- System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
- }
- public void sayq() {
- name = "qqqqqq";
- System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
- }
- }
- /**
- *
- Thread-1qqqqqq
- Thread-1qqqqqq
- Thread-0qqqqqq //其中一段错误信息,Thread-0线程也打印了qqqqqq
- Thread-0bbb
- Thread-0bbb
- */
问题出现过程:
- Thread-0获取执行权执行name="bbb"。
- Thread-1获得执行权执行name="qqqqqq"。
- Thread-0重新获得执行权执行打印指令,这时Thread-0就打印出了qqqqqq。
二、synchronized代码块
如果name赋值,打印name是一个原子操作就可以避免线程安全问题。
java中synchronized可以标记一段代码,达到原子操作的效果。
- 当一个线程执行标记有synchronized代码时将获得该对象的锁,然后开始执行synchronized标记的代码。
- 每一个对象只有一个锁,因此其他线程无法获得该对象锁。
- 其他线程如果这时候也执行到了标记有synchronized的代码将阻塞,直到获得对象锁的线程执行完synchronized标记的代码。
- 然后持有锁的线程释放锁。
- 其他线程开始争夺锁,回到第1步。
synchronized标记代码有两种方式:
- //synchronized代码块
- class Resource {
- private String name;
- public void sayb() {
- synchronized (this){
- name = "bbb";
- System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
- }
- //...其他代码
- }
- public void sayq() {
- synchronized (this){
- name = "qqqqqq";
- System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
- }
- //...其他代码
- }
- }
- //synchronized方法
- class Resource {
- private String name;
- public synchronized void sayb() {
- name = "bbb";
- System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
- //...其他代码
- }
- public synchronized void sayq() {
- name = "qqqqqq";
- System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
- //...其他代码
- }
- }
上例中两个线程执行的是同一个对象的方法,因此他们抢夺同一个锁,一个线程执行的时候,另一个线程阻塞。
两种方法有些不同点:
- synchronized方法标记在非static方法上,线程获得的锁为this,例子中为resource对象。若标记在static方法上则线程获得的锁为Resource.class对象。
- synchronized标记在代码块上,可以由用户自己指定,而且代码块的范围也可以自己指定。因此synchronized代码块比synchronized方法更加灵活。
注意:
- 使用synchronized会降低性能,使用时尽量缩小synchronized标记的范围。
- synchronized不会死锁,异常抛出时虚拟机会释放锁。
三、monitor与锁的重入
- class Test {
- public static void main(String[] args) {
- syn();
- }
- private static synchronized void syn(){
- synchronized (Test.class){
- synchronized (Test.class){
- synchronized (Test.class){
- System.out.println(1);
- }
- }
- }
- }
- }
- 虚拟机为每个对象分配了一个monitor。
- 前面说到当一个线程进入synchronized代码块,就获得了对象锁,实际上是monitor的计数器++。
- monitor的计数器>0时,线程获得锁,因此上面的代码monitor的计数器每次进入一个synchronized代码块monitor的计数器++,结束一个代码块monitor的计数器--。
- monitor的计数器=0时,线程才会释放锁。
这就是锁的重入。
java多线程-synchronized的更多相关文章
- Java 多线程 —— synchronized关键字
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java多线程synchronized同步
非线程安全问题 “非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程问题”.也即是说,方法中的变量永远是线程安全的. 如果多个线程共同访问1个对象中的实例变量,则可能线程 ...
- JAVA多线程synchronized详解
Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 当两个并发线程访问同一个对象object中的这个synchronized(this)同 ...
- java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解
synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...
- java 多线程 synchronized与lock的通信机制等问题,结合相应实例说明
1. 利用多线程实现如下需求: 写两个线程,一个线程打印1~52,另一个线程打印A~Z,打印顺序是12A34B...5152Z: 2. 使用synchronized 实现 public class T ...
- Java多线程-synchronized关键字
进程:是一个正在执行中的程序.每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者叫一个控制单元. 线程:就是进程中的一个独立的控制单元.线程在控制着进程的执行. 一个进程中至少有一个线程 Ja ...
- Java多线程synchronized关键字
synchronized关键字代表着同步的意思,在Java中被synchronized修饰的有三种情况 1.同步代码块 //锁为objsynchronized(obj){ while(true){ i ...
- Java 多线程 - Synchronized关键字
目录 1-Synchronized 关键字概述 2- Synchronized关键字作用域 3- Synchronized 原理(反编译指令解释) 正文 1-Synchronized 关键字概述 由于 ...
- java多线程:synchronized和lock比较浅析
转载:http://www.toutiao.com/a6392135944652587266/?tt_from=weixin&utm_campaign=client_share&app ...
随机推荐
- yum 安装telnet
检测是否安装 rpm -qa |grep telnet 安装 yum install xinetd yum install telnet-server yum -y install telnet 再次 ...
- 编写高质量代码改善C#程序的157个建议——建议16:元素数量可变的情况下不应使用数组
建议16:元素数量可变的情况下不应使用数组 在C#中,数组一旦被创建,长度就不能改变.如果我们需要一个动态且可变长度的集合,就应该使用ArrayList或List<T>来创建. 而数组本身 ...
- 综合学生信息管理系统(JSP+JDBC)
原创 通过JSP+JDBC制作一个简单的操作数据库中表信息的系统. 总体界面如下,一共有5个功能块. 功能一:列出全部学生 功能二:按条件查询学生 功能三:新添加学生 功能四:按条件删除学生 功能五: ...
- android MVP模式简单介绍
原文 http://zhengxiaopeng.com/2015/02/06/Android%E4%B8%AD%E7%9A%84MVP/ 前言 MVP作为一种MVC的演化版本在Android开发中受到 ...
- CSRF漏洞详细说明
CSRF漏洞详细说明 通常情况下,有三种方法被广泛用来防御CSRF攻击:验证token,验证HTTP请求的Referer,还有验证XMLHttpRequests里的自定义header.鉴于种种原因,这 ...
- unix网络编程str_cli使用epoll实现
unix网络编程str_cli使用epoll实现 unix环境高级编程中也有这个函数,都是为了讲解IO多路转接.从本质上来看epoll就是一个改善了的select和poll,本质没发生任何变化,对于构 ...
- 标准模板库使用参考——vector向量容器
C++的STL从广义上讲分为algorithm(算法),container(容器)和iterator(迭代器)三类,包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法. 在C++标准库中,ST ...
- Liunx常用的100条命令汇存
1.关机 shutdown -h now 立刻关机 poweroff shutdown -r now 立刻重启 reboot logout 注销 2.进入图形界面 startx 3.vi编辑器 [vi ...
- 常用跨平台IDE如何添加main函数的参数并正确执行
1. Eclipse-cdt如何添加main函数参数: 打开eclipse,新建工程.新建源文件,此处以C语言为例,写入代码如下: #include <stdio.h> //int arg ...
- error while loading shared libraies :libopencv_core_so.3.4:cannot open shared object
TX2 上安装自己编译的opencv,使用时出现: error while loading shared libraies :libopencv_core_so.3.4:cannot open sha ...