Java 多线程(二)—— 线程的同步
上文创建多线程买票的例子中注释会出现错票、重票的问题,本文来讲讲如何解决此问题。本文例子:利用多线程模拟 3 个窗口卖票
实现Runnable接口
public class TestThread2 {
public static void main(String [] args){
Window window=new Window();
Thread thread1=new Thread(window,"窗口一");
Thread thread2=new Thread(window,"窗口二");
Thread thread3=new Thread(window,"窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window implements Runnable{
int ticket=50;
@Override
public void run(){
while (true){
if(ticket > 0){
try {
Thread.currentThread().sleep(100);//模拟卖票需要一定的时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
}else {
break;
}
}
}
}
运行结果:
窗口二售票,票号为:13
窗口三售票,票号为:12
窗口一售票,票号为:11
窗口二售票,票号为:10
窗口一售票,票号为:10
窗口三售票,票号为:10
窗口三售票,票号为:9
窗口一售票,票号为:8
窗口二售票,票号为:7
窗口三售票,票号为:6
窗口一售票,票号为:5
窗口二售票,票号为:4
窗口三售票,票号为:3
窗口一售票,票号为:2
窗口二售票,票号为:1
窗口三售票,票号为:0
窗口一售票,票号为:-1
结果分析:这里出现了票数为0和负数还有重票的情况,这在现实生活中肯定是不存在的,那么为什么会出现这样的情况呢?
当票号为10时:A线程、B线程、C线程同时进入到if(ticket > 0)的代码块中,A线程已经执行了打印输出语句,但是还没有做ticket--操作;
这时B线程就开始执行了打印操作,那么就会出现两个线程打印票数一样,即卖的是同一张票
当票号为1时:A线程、B线程,C线程同时进入到if(ticket > 0)的代码块中,A线程执行了打印语句,并且已经做完了ticket--操作,则此时ticket=0;
B线程再打印时就出现了0的情况,同理C线程打印就会出现-1的情况。
解决办法:即我们不能同时让超过两个以上的线程进入到 if(ticket > 0)的代码块中,不然就会出现上述的错误。必须让一个线程操作共享数据完毕以后,其他线程才有机会参与共享数据的操作。我们可以通过以下两个办法来解决:
1、使用 同步代码块
2、使用 同步方法
使用 同步代码块
synchronized(同步监视器){
//需要被同步的代码块(即为操作共享数据的代码)
}
同步监视器:由任意一个类的对象来充当,哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁
要求:1、所有的线程必须公用同一把锁!不能相对于线程是变化的对象;
2、并且只需锁住操作共享数据的代码,锁多了或少了都不行;
实例:
1、实现的方式
public class TestWindow {
public static void main(String [] args){
Window1 window=new Window1();
Thread thread1=new Thread(window,"窗口一");
Thread thread2=new Thread(window,"窗口二");
Thread thread3=new Thread(window,"窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window1 implements Runnable{
int ticket=100;//共享数据
@Override
public void run(){
while (true){
synchronized (this){//this表示当前对象,此时表示创建的 window
if(ticket > 0){
try {
//模拟卖票需要一定的时间
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
}
}
}
}
}
注意:在实现的方式中,考虑同步的话,可以使用this充当锁,但在继承的方式中,会创建多个对象,慎用this
2、继承的方式
public class TestWindow1 {
public static void main(String [] args){
Window2 window1=new Window2();
Window2 window2=new Window2();
window1.start();
window2.start();
}
}
class Window2 extends Thread{
static int ticket=100;//共享数据;注意声明为 static,表示几个窗口共享
static Object object=new Object();//用static 可以表示唯一
@Override
public void run(){
while (true){
//synchronized (this){//this表示当前对象,此时表示创建的 window1和window2
synchronized (object){//锁必须是唯一,不能每个线程都使用自己的一把锁
if(ticket > 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
}
}
}
}
}
注意:1、继承的方式会创建多个实例,所以共享资源需要用static来修饰,表示共享
2、继承的方式会创建多个实例,所以 this 表示不同的实例对象,这里表示widow1和window2,所以不能使用this当锁,此时可以定义一个 static 修饰的对象当锁
使用 同步方法
语法:即用 synchronized 关键字修饰方法
将操作共享数据的方法声明为synchronized。即此方法为同步方法,能够保证当其中一个线程执行此方法时,其他线程再外等待直至此线程执行完此方法。
注意:同步方法的锁:this
实例:
1、实现的方式
public class TestWindow2 {
public static void main(String [] args){
Window3 window=new Window3();
Thread thread1=new Thread(window,"窗口一");
Thread thread2=new Thread(window,"窗口二");
Thread thread3=new Thread(window,"窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
class Window3 implements Runnable{
int ticket=100;//共享数据
@Override
public void run(){
while (true){
show();
}
}
public synchronized void show(){//this充当锁,此时表示创建的 window;
// 如果用继承的方式,使用同步方法,这里表示创建的 window1和window2,继承的方式不要使用同步方法
if(ticket > 0){
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket--);
}
}
}
注意:1、synchronized 的锁为this,这里表示创建的对象实例window;
2、继承的时候t this 表示创建的window1和window2,继承的方式不要使用同步方法。
Java 多线程(二)—— 线程的同步的更多相关文章
- Java多线程之线程的同步
Java多线程之线程的同步 实际开发中我们也经常提到说线程安全问题,那么什么是线程安全问题呢? 线程不安全就是说在多线程编程中出现了错误情况,由于系统的线程调度具有一定的随机性,当使用多个线程来访问同 ...
- java多线程之线程的同步与锁定(转)
一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏. 例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. publicc ...
- (原创)JAVA多线程二线程池
一,线程池的介绍 线程池包括一下三种: 线程池名称 创建方法 特点 其他 固定大小线程池 ExecutorService threadpool = Executors.newFixedThreadPo ...
- 关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇高质量的博文)
Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享 ...
- Java多线程与线程同步
六.多线程,线程,同步 ①概念: 并行:指两个或多个在时间同一时刻发生(同时发生) 并发:指两个或多个事件在同一时间段内发生 具体概念: 在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多 ...
- Java多线程02(线程安全、线程同步、等待唤醒机制)
Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...
- JAVA多线程之线程间的通信方式
(转发) 收藏 记 周日,北京的天阳光明媚,9月,北京的秋格外肃穆透彻,望望窗外的湛蓝的天,心似透过栏杆,沐浴在这透亮清澈的蓝天里,那朵朵白云如同一朵棉絮,心意畅想....思绪外扬, 鱼和熊掌不可兼得 ...
- Java多线程之线程的生命周期
Java多线程之线程的生命周期 一.前言 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(R ...
- Java多线程之线程其他类
Java多线程之线程其他类 实际编码中除了前面讲到的常用的类之外,还有几个其他类也有可能用得到,这里来统一整理一下: 1,Callable接口和Future接口 JDK1.5以后提供了上面这2个接口, ...
- Java多线程之线程的通信
Java多线程之线程的通信 在总结多线程通信前先介绍一个概念:锁池.线程因为未拿到锁标记而发生的阻塞不同于前面五个基本状态中的阻塞,称为锁池.每个对象都有自己的锁池的空间,用于放置等待运行的线程.这些 ...
随机推荐
- Linux-day1-上课笔记
命令的组成 命令关键字 [选项] [参数] 注意: 1. 通常情况下 选项- --连接 ls -l /etc 2. - 选项和选项之间是可以合并的 ls -ld /etc ls 罗列文件 常见的选 ...
- HYPER -V 独立安装的 2016版本 中文版 下载好慢啊
HYPER -V 独立安装的 2016版本 中文版 下载好慢啊
- QEMU, a Fast and Portable Dynamic Translator-Fabrice Bellard-翻译
Abstract We present the internals of QEMU, a fast machine emulator using an original portable dynami ...
- 意识科学初步:David Chalmers的简单问题与困难问题
这是第一篇关于意识科学的内容.主要谈一下阅读大卫查莫斯的几篇论文的一些观点和思考. 论文作者简介(摘自wiki): David John Chalmers (born 20 April 1966) i ...
- Linux下Shell重定向
1. 标准输入,标准输出与标准错误输出 Linux下系统打开3个文件,标准输入,标准输出,标准错误输出. 标准输入:从键盘输入数据,即从键盘读入数据. 标准输出:把数据输出到终端上. 标准错误输出:把 ...
- VS之设置文件编码格式
VS2012默认格式为 "GB2312-80",很多时候可能出现乱码情况,就是编码问题,如何在VS里修改呢? 文件->“高级保存选项 ” 选择gb2312
- 如何解决PeopleSoft Process Scheduler发布问题
常见PeopleSoft进程调度程序发布问题 此发布问题中遇到的一些常见错误是: 将HTTP响应转换为UCS2时出错 XML文档对象创建失败. 无法处理来自Report Repository的HTTP ...
- XP .Net 4.0使用Microsoft.Bcl需要安装XP补丁NDP40-KB2468871-v2-x86
XP .Net 4.0使用Microsoft.Bcl需要安装XP补丁NDP40-KB2468871-v2-x86
- django+javascrpt+python实现私有云盘
代码稍后上,先整理下私有云盘的相关功能介绍. 1.登陆界面 2.首页展示,有个人目录.部门目录以及公司目录,针对不用的目录设置不同的权限控制. 3.个人信息展示 4.账号管理.账号信息展示 5.账号添 ...
- Java for Android 第三周学习总结
第五章 核心类 java.lang.Object中的方法: clone(创建并返回该对象的一个副本.实现这个方法的一个类,将支持对象的复制) equals(将该对象和传入的对象进行比较.必须实现这个算 ...