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(多线程)一样

转载于:https://www.cnblogs.com/sonng/p/6134444.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/417651.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

我的.Net+SQLServer更新设计

一、备份 备份程序、数据库(非常重要) 二、更新程序 发布Web/Winform程序,将发布文件夹中需要更新的文件发布到服务器上,这里配置文件如果有修改应该单 独修改,否则不应更新配置文件。 三、更新数据库架构 可以通过VS2…

numpy将所有数据变为0和1_Numpy库学习

之前照着《利用python进行数据分析》学过一边,代码也跟着敲了,但是如果不用的话,印象不深,忘的特别快。所以,现在再过一遍,并且记录一下,加深印象。一、创建numpy中的多维数组对象ndarry的创建。…

win8.1 linux系统,电脑显示win8.1linux系统失效的原因及解决办法!

电脑蓝屏每次代码都不一样,有的是epoll或者workflow代码有的是错误码,会将错误进行查找,电脑蓝屏错误有些可以修复,也有错误修复不了的,出现蓝屏,最可能的原因是segmentationfault,出现的原因是…

js 保留两位小数(四舍五入)

toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。 intA.toFixed(2)

数据丢失情况下应急之顶级数据恢复服务提供商列表

在当今的数字世界中,数据存储设备在我们的职业和个人生活中发挥着非常重要的作用。 无论是硬盘驱动器上的重要专业文档、USB 闪存驱动器上的医疗数据还是外部硬盘驱动器上的照片和视频,我们都将重要信息存储在一个或另一个数据存储设备上。我们认为数据…

jsencrypt加密同一值返回不同密文_密码学原语如何应用?解析密文同态性的妙用...

免责声明:本文旨在传递更多市场信息,不构成任何投资建议。文章仅代表作者观点,不代表火星财经官方立场。小编:记得关注哦来源:微众银行区块链隐私数据在密文形式下是否依旧可以加减乘除?其背后的同态性原理…

c语言函数大全 chm,【oeasy丨c语言丨函数】C语言库函数使用大全CHM版

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼函数名: arc功 能: 画一弧线用 法: void far arc(int x, int y, int stangle, int endangle, int radius);程序例:#include #include #include #include int main(void){/* request auto detection */int gdriver DETECT, gmode, …

VS.NET 编译出来的DLL,XML注释(Sumarry 注释)输出

项目属性——生成——输出中选中xml文档文件——生成即可

小红帽怎样装图形化界面_纯技术篇:U盘装系统,不再多花冤枉钱

U盘装系统,顾名思义就是用U盘安装电脑的操作系统。这里小编用现在最常见的大白菜装机软件来开始教程。1、使用大白菜装机版制作大白菜U盘启动盘之前,需要准备一个存储空间大于2G的U盘。2、下载并且安装好大白菜装机版,打开安装好的大白菜装机…

c#语言中的变量名,在C#中创建动态变量名

如果你坚持使用当前的设计(CSV字典),你可以使用ExpandoObject类来获得你想要的东西,创建一个简单的工厂类:public static class ObjectFactory{public static dynamic CreateInstance(Dictionary objectFromFile){dynamic instance new ExpandoObject();var instan…

c++11 lambda

为什么需要lambda函数匿名函数是许多编程语言都支持的概念,有函数体,没有函数名。1958年,lisp首先采用匿名函数,匿名函数最常用的是作为回调函数的值。正因为有这样的需求,c引入了lambda 函数,你可以在你的…

Axure SVN共享 Commit Failed (details Follow) 问题解决

1.先全部获新,再签入。2.重新连接共享获新到另一个文件夹,重新修改签入即可。

c语言编译器不支持64位,什么编译器支持64位整数?

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼Free downloadsWhat Description Other infoslcc-win32 The compiler system, featuring compiler, linker, assembler, resource Editor and a lot of useful tools size: 4.0M last updated:Saturday, 09-Oct-2004 07:18:23 EDT)…

北斗三号b1c频点带宽_重磅北斗星通新一代22nm北斗高精度定位芯片正式发布

11月23日,第十一届中国卫星导航年会在四川省成都市盛大召开。年会首日,北斗星通旗下企业和芯星通正式发布了最新一代全系统全频厘米级高精度GNSS芯片——和芯星云NebulasⅣ。这颗芯片代表了国内卫星导航芯片的最高水平,在厘米级高精度定位领域…

算法随笔一(背包问题)

今天逛园子,偶然看到了“背包问题”,于是上网找了下相关资料,并写了个简单的实现方案。 何为背包问题? 简单理解,就是给了一堆物品跟一个包,每个物品都有相应的重量和价值,包有自己的承重。我们…

js Array扩展方法

Object.extend function (destination, source) { /// <summary> /// 扩展对象方法 /// </summary> for (var property in source) { destination[property] source[property]; } return destination;} Object.extend(Array.prototype, { …

c语言编程求5的阶乘传统流程图,C语言算法第五源代码以及流程图.doc

C语言算法第五源代码以及流程图程序流程图 内存 执行过程 数计学院计算机科学与技术 110座机电话号码6戴晨丹1 /* example5-3.cpp */2 #include3 Int main45 Intx,y, max;6 printf “请输入两个整数:” ;7 scanf “%d%d”, &x, &y ;8 If x y9 max x;10 else11 max y;12…