0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步
- 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条线程访问,一条线程在执行一个循环的过程中被中断,下一个线程则出现错误
- 因此,线程任务中可能引起错误的地方应当被一次执行完毕
同步代码块
- 用同步代码块改写上面的代码
package testpack;
public class Test1 {
public static void main(String[] args){
System.out.println("现在是主线程: "+Thread.currentThread());
System.out.println("下面新建两个线程");
A a=new A(500);
new Thread(a,"线程A").start();
new Thread(a,"线程B").start();
new Thread(a,"线程C").start();
}
}
class A implements Runnable{
private int tickets;
A (int tick){
tickets=tick;
}
private Object obj=new Object(); //同步监视器
public void run() {
synchronized(obj){ //同步代码块
for (;tickets>0;tickets--) {
System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");
try{
Thread.sleep(1); //让当前线程暂停1毫秒,其他线程也不能执行该同步代码块
}catch(InterruptedException ex){
ex.printStackTrace();
}
if (tickets==1){
System.out.println("票已卖完,当前线程是: "+Thread.currentThread());
}
}
}
}
}
- 同步监视器,就是一个普通的对象,就像一把锁,只有获得了同步监视器的线程才能执行同步代码块。
- 同步代码块执行一次完毕后,将会释放锁,接下来是这条线程拿到同步锁,还是其他其他线程,则不一定,根据线程调度而定,但是在同步代码块执行过程中,不会被中断,一个同步任务会被一次执行完毕
同步方法
- 在同步代码块中,synchonized关键字在run()方法内部,修饰的是一段代码,也可以用来修饰run()方法,也就是同步方法
- synchronized不只可以修饰run()方法,还可以修饰其他方法,只要是需要一次同步完成的任务,然后再在run()方法中被调用
- 同步方法中有一个隐式的同步监视器,就是this,也就是调用run()方法(或同步方法)的这个对象
- 还是上面的实例,用同步方法改写
package testpack;
public class Test1 {
public static void main(String[] args){
System.out.println("现在是主线程: "+Thread.currentThread());
System.out.println("下面新建两个线程");
A a=new A(500);
new Thread(a,"线程A").start();
new Thread(a,"线程B").start();
new Thread(a,"线程C").start();
}
}
class A implements Runnable{
private int tickets;
A (int tick){
tickets=tick;
}
public synchronized void run() { //同步方法
for (;tickets>0;tickets--) {
System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");
try{
Thread.sleep(1);
}catch(InterruptedException ex){
ex.printStackTrace();
}
if (tickets==1){
System.out.println("票已卖完,当前线程是: "+Thread.currentThread());
}
}
}
}
释放同步监视器
- 当前线程的同步任务(同步方法、同步代码块)执行完毕
- 在同步任务中,遇到break、return,终止了同步任务
- 在同步任务中,出现Error、Exception等,导致同步任务结束
- 在同步任务中,执行了同步监视器对象的wait()方法,则当前线程暂停,并释放同步监视器
- 不会释放同步监视器的情况:
- 同步任务中,调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行
- 同步任务中,其他线程调用了该线程的suspend()方法将该线程挂起
同步锁
- 除了可以用new Object()和this作同步监视器往外,还可以定义专门的同步锁,且功能更加强
- Lock接口
- ReentrantLock实现类
- ReadWriteLock
- ReentrantReadWriteLock实现类
- ReentrantReadWriteLock.ReadLock
- ReentrantReadWriteLock.WriteLock
- StampedLock
- 示例:用ReentrantLock改写上面的代码
package testpack;
import java.util.concurrent.locks.ReentrantLock;
public class Test1 {
public static void main(String[] args){
System.out.println("现在是主线程: "+Thread.currentThread());
System.out.println("下面新建两个线程");
A a=new A(50);
new Thread(a,"线程A").start();
new Thread(a,"线程B").start();
new Thread(a,"线程C").start();
}
}
class A implements Runnable{
private final ReentrantLock lock=new ReentrantLock(); //定义一个同步锁
private int tickets;
A (int tick){
tickets=tick;
}
public void run() {
lock.lock(); //加锁
for (;tickets>0;tickets--) {
System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");
if (tickets==1){
System.out.println("票已卖完,当前线程是: "+Thread.currentThread());
}
}
lock.unlock(); //释放锁
}
}
死锁
- 两个线程各拿一把锁,下一步运行都需要对方手里那把锁,但都拿不到,则造成死锁,程序不能继续执行
package testpack;
public class Test1 {
public static void main(String[] args){
DeadLock dl=new DeadLock();
new Thread(dl).start();
dl.init();
}
}
class DeadLock implements Runnable {
A a=new A();
B b=new B();
public void init(){
a.a1(b);
System.out.println("进入主线程");
}
public void run(){
b.b1(a);
System.out.println("进入子线程");
}
}
class A {
public synchronized void a1(B b){
System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,正在执行a1()");
try{
Thread.sleep(10);
}catch(InterruptedException ex){
ex.printStackTrace();
}
System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,准备调用b2()");
b.b2(); //b2方法是同步方法,调用该方法要对调用的对象b加锁
}
public synchronized void a2(){
System.out.println("这是a2()方法");
}
}
class B{
public synchronized void b1(A a){
System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,正在执行b1()");
try{
Thread.sleep(10);
}catch(InterruptedException ex){
ex.printStackTrace();
}
System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,准备调用a2()");
a.a2(); //a2方法是同步方法,调用该方法要对调用的对象a加锁
}
public synchronized void b2(){
System.out.println("这是b2()方法");
}
}
- 上面在调用a.a2()和b.b2()方法时,分别要对a对象和b对象加锁,但这时,a、b对象的锁都在对方手里,造成两个线程阻塞
其他
- 可变类的线程安全是以降低程序的运行效率为代价的
- 不要对线程安全类的所有方法都进行同步,只对那些改变共享资源的方法进行同步
- 如果一个类有单线程和多线程运行环境,那么应该提供两种版本,就是StringBuilder(单线程)和StringBuffer(多线程)一样
0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁的更多相关文章
- 线程同步 synchronized 同步代码块 同步方法 同步锁
一 同步代码块 1.为了解决并发操作可能造成的异常,java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块.其语法如下: synchronized(obj){ // ...
- 0023 Java学习笔记-面向对象-初始化代码块
初始化代码块 在18篇-类的基本要素中说到,类的三大成员:成员变量.构造方法.方法,初始化代码块是类的第4个成员 初始化块用于对类或者对象的初始化, 一个类的初始化块可以有0-多个,按先后顺序执行 跟 ...
- Java学习笔记 - 类方法与代码块的执行顺序
类的初始化顺序 使用一个简单的父子类例子来做示范,代码执行顺序在代码后有标注. class Parent { public static String p_StaticField = "父类 ...
- JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this
JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- 线程的同步机制:同步代码块&同步方法
解决存在的线程安全问题:打印车票时出现重票,错票 使用同步代码块的解决方案 TestWindow2 package com.aff.thread; /* 使用实现Runnable接口的方式,售票 存在 ...
- java学习笔记 --- 多线程(线程安全问题——同步代码块)
1.导致出现安全问题的原因: A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 2.解决线程安全问题方法: 同步代码块: synchronized(对象){ 需要同步的代码; ...
- 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题
调用同步锁的wait().notify().notifyAll()进行线程通信 看这个经典的存取款问题,要求两个线程存款,两个线程取款,账户里有余额的时候只能取款,没余额的时候只能存款,存取款金额相同 ...
- JAVA学习笔记 -- 多线程之共享资源
在多线程程序执行过程中,可能会涉及到两个或者多个线程试图同一时候訪问同一个资源.为了防止这样的情况的发生,必须在线程使用共享资源时给资源"上锁",以阻挡其他线程的訪问. 而这样的机 ...
随机推荐
- SQL 邮件配置篇
在我们运维工作中,经常要对备份,ETL等作业进行监控,这时我们需要用到SQL SERVER自带的邮件服务器,其原理,我在这么里不多说,直接来实战,下面是我对服务器配置源码,分享给大家,希望对大家有帮助 ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- JavaScript权威设计--事件冒泡,捕获,事件句柄,事件源,事件对象(简要学习笔记十八)
1.事件冒泡与事件捕获 2.事件与事件句柄 3.事件委托:利用事件的冒泡技术.子元素的事件最终会冒泡到父元素直到跟节点.事件监听会分析从子元素冒泡上来的事件. 事件委托的好处: 1.每个函 ...
- Hadoop的体系结构
HDFS和MapReduce是Hadoop的两大核心.而整个Hadoop的体系结构主要是通过HDFS来实现对分布式存储的底层支持的,并且它会通过MapReduce来实现对分布式并行任务处理的程序支持. ...
- 添加 Pool Member - 每天5分钟玩转 OpenStack(123)
我们已经有了 Load Balance Pool "web servers"和 VIP,接下来需要往 Pool 里添加 member 并学习如何使用 cloud image. 先准 ...
- DDD 领域驱动设计-谈谈 Repository、IUnitOfWork 和 IDbContext 的实践(2)
上一篇:<DDD 领域驱动设计-谈谈 Repository.IUnitOfWork 和 IDbContext 的实践(1)> 阅读目录: 抽离 IRepository 并改造 Reposi ...
- ASP.NET MVC5下载数据到Excel文件
项目中的一个功能是将数据导入到Excel文件中,这里使用NPOI操作Excel,代码如下: public class Excel : IDataTransfer { public Stream Exp ...
- Asp.Net Core 项目实战之权限管理系统(5) 用户登录
0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...
- 基于NodeJS微信公众号
最近重新研究了微信公众号的高级接口,原来也利用C#或JAVA写过微信公众号,主要是消息的基础接口. 由于当时不知道微信公众号可以申请测试公众号,微信测试公众号基本上没有任何限制,对于开发来说是一个不错 ...
- 基于STM32Cube的IIC主从通信
1.建立STM32Cube工程,我使用的芯片是STM32F429,为了更简单的建立单独的IIC通信任务,我使用了FREERTOS,另外选择了RCC(系统时钟初始化),I2C1,I2C2(为了验证代码的 ...