Java基础-多线程-③线程同步之synchronized
使用线程同步解决多线程安全问题
上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程也对数据进行了操作,从而导致数据出错。由此我们想到一个解决的思路:将操作共享数据的代码行作为一个整体,同一时间只允许一个线程执行,执行过程中其他线程不能参与执行。线程同步就是用来实现这样的机制。
synchronized代码块
Java中提供了synchronized关键字,将可能引发安全问题的代码包裹在synchronized代码块中,表示这些代码需要进行线程同步。synchronized代码块的语法格式为:
synchronized (expression){
//需要同步的代码
}
其中,expression必须是一个引用类型的变量,这里我们可以理解为任意的一个Java对象,否则编译出错。下面的例子中我们使用了一个Object对象obj。
class Dog implements Runnable {
private int t = 100;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (t > 0) {
try {
Thread.sleep(100);
System.out.println("当前线程:" + Thread.currentThread().getName() + "---" + t--);
} catch (InterruptedException e) {}
}
}
}
}
}
这时候,一个线程在执行完整个代码块(或者非正常结束)之后,其他的线程才有机会进入代码块执行,就不会出现“打印的t小于1”的情况了,简单的实现了代码的同步。
线程同步的机制和同步锁
上面线程同步的效果是怎么实现的呢?Java中任意的对象都可以作为一个监听器(monitor),监听器可以被上锁和解锁,在线程同步中称为同步锁,且同步锁在同一时间只能被一个线程所持有。上面的obj对象就是一个同步锁,分析一下上面代码的执行过程:
- 一个线程执行到synchronized代码块,首先检查obj,如果obj为空,抛出NullPointerExpression异常;
- 如果obj不为空,线程尝试给监听器上锁,如果监听器已经被锁,则线程不能获取到锁,线程就被阻塞;
- 如果监听器没被锁,则线程将监听器上锁,并且持有该锁,然后执行代码块;
- 代码块正常执行结束或者非正常结束,监听器都将自动解锁;
所以,一个线程执行代码块时,持有了同步锁,其他线程就不能获取到锁,也就不能进入代码块执行,只能等待锁被释放。这时候我们思考这样一个问题:在synchronized代码块中如果我们每次传入的都是一个新的对象,能否实现同步的效果呢?如下:
public void run() {
while (true) {
synchronized (new Object()) {
if (t > 0) {
try {
Thread.sleep(100);
System.out.println("当前线程:" + Thread.currentThread().getName() + "---" + t--);
} catch (InterruptedException e) {}
}
}
}
}
显然多个线程检查的都是一个新的对象,不同的同步锁对不同的线程不具有排他性,不能实现线程同步的效果,这时候线程同步就失效了。所以线程同步的一个前提:线程同步锁对多个线程必须是互斥的,即多个线程需要使用同一个同步锁。第一段代码中obj对象被多个线程共享,能够实现同步。
synchronized方法
除了synchronized代码块,synchronized关键字还可以修饰方法,让该方法进行线程同步,效果跟同步代码块一样。
public synchronized void run() {
while (true) {
if (t > 0) {
try {
Thread.sleep(100);
System.out.println("当前线程:" + Thread.currentThread().getName() + "---" + t--);
} catch (InterruptedException e) {}
}
}
}
这时候synchronized后面没有了expression,从哪儿获取同步锁呢?
- 对于实例的同步方法,使用this即当前实例对象,如上面的dog;
- 对于静态的同步方法,使用当前类的字节码对象,如上面的Dog.class。
也就是说使用同步方法的话,同步锁只能是this或者当前类的字节码对象。所以根据同步锁必须互斥的前提,如果同时使用synchronized代码块和synchronized方法对同一个共享资源进行线程同步,synchronized代码块的同步锁也必须跟synchronized方法一样(要么是this,要么是类的字节码对象)。
同步代码块和同步方法的区别
两者的区别主要体现在同步锁上面。对于实例的同步方法,因为只能使用this来作为同步锁,如果一个类中需要使用到多个锁,为了避免锁的冲突,必然需要使用不同的对象,这时候同步方法不能满足需求,只能使用同步代码块(同步代码块可以传入任意对象);或者多个类中需要使用到同一个锁,这时候多个类的实例this显然是不同的,也只能使用同步代码块,传入同一个对象。
Java基础-多线程-③线程同步之synchronized的更多相关文章
- Java基础教程——线程同步
线程同步 synchronized:同步的 例:取钱 不做线程同步的场合,假设骗子和户主同时取钱,可能出现这种情况: [骗子]取款2000:账户余额1000 [户主]取款2000:账户余额1000 结 ...
- java并发之线程同步(synchronized和锁机制)
使用synchronized实现同步方法 使用非依赖属性实现同步 在同步块中使用条件(wait(),notify(),notifyAll()) 使用锁实现同步 使用读写锁实现同步数据访问 修改锁的公平 ...
- Java基础8-多线程;同步代码块
作业解析 利用白富美接口案例,土豪征婚使用匿名内部类对象实现. interface White{ public void white(); } interface Rich{ public void ...
- Java基础-多线程-①线程的创建和启动
简单阐释进程和线程 对于进程最直观的感受应该就是“windows任务管理器”中的进程管理: (计算机原理课上的记忆已经快要模糊了,简单理解一下):一个进程就是一个“执行中的程序”,是程序在计算机上的一 ...
- java基础-多线程线程池
线程池 * 程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互.而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池.线程池里的每一个线程代 ...
- java基础-多线程-线程组
线程组 * Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制. * 默认情况下,所有的线程都属于主线程组. * public fi ...
- Java基础-多线程-②多线程安全问题
什么是线程的安全问题? 上一篇 Java基础-多线程-①线程的创建和启动 我们说使用实现Runnable接口的方式来创建线程,可以实现多个线程共享资源: class Dog implements Ru ...
- java基础-多线程应用案例展示
java基础-多线程应用案例展示 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.两只熊,100只蜜蜂,蜜蜂每次生产的蜂蜜量是1,罐子的容量是30,熊在罐子的蜂蜜量达到20的时候 ...
- Java并发包——线程同步和锁
Java并发包——线程同步和锁 摘要:本文主要学习了Java并发包里有关线程同步的类和锁的一些相关概念. 部分内容来自以下博客: https://www.cnblogs.com/dolphin0520 ...
随机推荐
- 基于 OpenSSL 的 CA 建立及证书签发
http://rhythm-zju.blog.163.com/blog/static/310042008015115718637/ 建立 CA 建立 CA 目录结构 按照 OpenSSL 的默认配置建 ...
- 为cobbler自动化安装系统工具添加epel源
关于cobbler的安装及部署,参考:CentOS 6.5自动化运维之基于cobbler服务的自动化安装操作系统详解http://blog.csdn.net/reblue520/article/det ...
- linux 创建用户和密码
:useradd -m 用户名//添加用户 :passwd 用户名 //然后设置密码 :userdel -r newuser1 //删除用户 newuser1,同时删除其自家目录 samba 设置账号 ...
- 线段树解LIS
先是nlogn的LIS解法 /* LIS nlogn解法 */ #include<iostream> #include<cstring> #include<cstdio& ...
- 性能测试十六:liunx下jmete配置环境变量
修改环境变量后就不用每次手动输入路径,省时省事,减少命令长度和出错率 按Ctrl+L可进行翻页,翻页到最后一行,此处有java的环境变量 添加jmeter的目录和bin目录 此时,虽修改成功,但是并未 ...
- python 全栈开发,Day127(app端内容播放,web端的玩具,app通过websocket远程遥控玩具播放内容,玩具管理页面)
昨日内容回顾 1. 小爬爬 内容采集 XMLY 的 儿童频道 requests 2. 登陆 注册 自动登陆 退出 mui.post("请求地址",{数据},function(){} ...
- 2018-2019-2 网络对抗技术 20165333 Exp2 后门原理与实践
实验内容 1.使用netcat获取主机操作Shell,cron启动 2.使用socat获取主机操作Shell, 任务计划启动 3.使用MSF meterpreter(或其他软件)生成可执行文件,利用n ...
- POJ 1065 Wooden Sticks【贪心】
题意: 有一些木棍,每个有长度和重量,要求把这些木棍排成若干两个属性值均不下降的序列.问至少要分为多少个序列.且要保证排出来的子序列数最少. 思路: ( 9 , 4 ) ,( 2 , 5 ) ,( 1 ...
- BZOJ2287【POJ Challenge】消失之物
题解: 1.以前见过类似的,可以cdq分治 当l=r时就是还有一个剩余 这样时间是nmlogn的 空间是mlogn 2.首先我们可以dp出表示出j的方案数 令g[i][j]表示不能选i,表示出j的方案 ...
- #2 codeforces 480 Parcels
题意: 就是有一个用来堆放货物的板,承重力为S.现在有N件货物,每件货物有到达的时间,运走的时间,以及重量,承重,存放盈利.如果这件货物能再运达时间存放,并在指定时间取走的话,就能获得相应的盈利值.货 ...