java 多线程 3 synchronized 同步
多任务编程的难点在于多任务共享资源。对于同一个进程空间中的多个线程来说,它们都共享堆中的对象。某个线程对对象的操作,将影响到其它的线程。
在多线程编程中,要尽力避免竞争条件(racing condition),即运行结果依赖于不同线程执行的先后。线程是并发执行的,无法确定线程的先后,所以我们的程序中不应该出现竞争条件。
然而,当多任务共享资源时,就很容易造成竞争条件。我们需要将共享资源,并造成竞争条件的多个线程线性化执行,即同一时间只允许一个线程执行。
(可更多参考Linux多线程与同步)
下面是一个售票程序。3个售票亭(Booth)共同售卖100张票(Reservoir)。每个售票亭要先判断是否有余票,然后再卖出一张票。如果只剩下一张票,在一个售票亭的判断和售出两个动作之间,另一个售票亭卖出该票,那么第一个售票亭(由于已经执行过判断)依然会齿形卖出,造成票的超卖。为了解决该问题,判断和售出两个动作之间不能有“空隙”。也就是说,在一个线程完成了这两个动作之后,才能有另一个线程执行。
在Java中,我们将共享的资源置于一个对象中,比如下面r(Reservoir)对象。它包含了总共的票数;将可能造成竞争条件的,针对共享资源的操作,放在synchronized(同步)方法中,比如下面的sellTicket()。synchronized是方法的修饰符。在Java中,同一对象的synchronized方法只能同时被一个线程调用。其他线程必须等待该线程调用结束,(余下的线程之一)才能运行。这样,我们就排除了竞争条件的可能。
在main()方法中,我们将共享的资源(r对象)传递给多个线程:
public class Test
{
public static void main(String[] args)
{
Reservoir r = new Reservoir(100);
Booth b1 = new Booth(r);
Booth b2 = new Booth(r);
Booth b3 = new Booth(r);
}
} /**
* contain shared resource
*/
class Reservoir {
private int total; public Reservoir(int t)
{
this.total = t;
} /**
* Thread safe method
* serialized access to Booth.total
*/
public synchronized boolean sellTicket()
{
if(this.total > 0) {
this.total = this.total - 1;
return true; // successfully sell one
}
else {
return false; // no more tickets
}
}
} /**
* create new thread by inheriting Thread
*/
class Booth extends Thread {
private static int threadID = 0; // owned by Class object private Reservoir release; // sell this reservoir
private int count = 0; // owned by this thread object
/**
* constructor
*/
public Booth(Reservoir r) {
super("ID:" + (++threadID));
this.release = r; // all threads share the same reservoir
this.start();
} /**
* convert object to string
*/
public String toString() {
return super.getName();
} /**
* what does the thread do?
*/
public void run() {
while(true) {
if(this.release.sellTicket()) {
this.count = this.count + 1;
System.out.println(this.getName() + ": sell 1");
try {
sleep((int) Math.random()*100); // random intervals
}
catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
else {
break;
}
}
System.out.println(this.getName() + " I sold:" + count);
}
}
Java的每个对象都自动包含有一个用于支持同步的计数器,记录synchronized方法的调用次数。线程获得该计数器,计数器加1,并执行synchronized方法。如果方法内部进一步调用了该对象的其他synchronized方法,计数器加1。当synchronized方法调用结束并退出时,计数器减1。其他线程如果也调用了同一对象的synchronized方法,必须等待该计数器变为0,才能锁定该计数器,开始执行。Java中的类同样也是对象(Class类对象)。Class类对象也包含有计数器,用于同步。
java 多线程 3 synchronized 同步的更多相关文章
- java 多线程并发 synchronized 同步机制及方式
2. 锁机制 3. 并发 Excutor框架 4. 并发性与多线程介绍 1. synchronized 参考1. synchronized 分两种方式进行线程的同步:同步块.同步方法 1. 方法同步 ...
- 四、java多线程核心技术——synchronized同步方法与synchronized同步快
一.synchronized同步方法 论:"线程安全"与"非线程安全"是多线程的经典问题.synchronized()方法就是解决非线程安全的. 1.方法内的变 ...
- 关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇高质量的博文)
Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享 ...
- Java多线程-线程的同步(同步方法)
线程的同步是保证多线程安全访问竞争资源的一种手段.线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些 ...
- java多线程:线程同步synchronized(不同步的问题、队列与锁),死锁的产生和解决
0.不同步的问题 并发的线程不安全问题: 多个线程同时操作同一个对象,如果控制不好,就会产生问题,叫做线程不安全. 我们来看三个比较经典的案例来说明线程不安全的问题. 0.1 订票问题 例如前面说过的 ...
- Java多线程 3 线程同步
在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系.可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题.现在就来学习多线程对数据访问的 ...
- Java多线程-线程的同步与锁
一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏.例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. package ...
- java中的synchronized同步代码块和同步方法的区别
下面这两段代码有什么区别? //下列两个方法有什么区别 public synchronized void method1(){} public void method2(){ synchronized ...
- Java多线程与线程同步
六.多线程,线程,同步 ①概念: 并行:指两个或多个在时间同一时刻发生(同时发生) 并发:指两个或多个事件在同一时间段内发生 具体概念: 在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多 ...
随机推荐
- Hadoop-2.2.0中文文档—— 从Hadoop 1.x 迁移至 Hadoop 2.x
简单介绍 本文档对从 Apache Hadoop 1.x 迁移他们的Apache Hadoop MapReduce 应用到 Apache Hadoop 2.x 的用户提供了一些信息. 在 Apache ...
- thinkphp session如何取数组
thinkphp session如何取数组 session('user_auth.username'); 搞定!
- jQuery Event.which 属性详解
jQuery Event.which 属性详解 which属性用于返回触发当前事件时按下的键盘按键或鼠标按钮. 对于键盘和鼠标事件,该属性用于确定你按下的是哪一个键盘按键或鼠标按钮. which属性对 ...
- 【LeetCode】97. Interleaving String
Interleaving String Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. Fo ...
- 【LeetCode】99. Recover Binary Search Tree
Recover Binary Search Tree Two elements of a binary search tree (BST) are swapped by mistake. Recove ...
- windows上通过secureCRT和putty创建密钥登录
前面介绍了linux的ssh远程登录协议和ssh无password登录方式.这里在windows下通过secureCRT和putty登录linux来看一下详细的密钥创建,配置和登录.也算做个备忘录吧. ...
- tp配置+路由+基本操作
一.打开apache 的配置文件httpd_conf添加以下代码 <VirtualHost *:80>DocumentRoot "D:\wwwroot\thinkphp\publ ...
- suid/guid用法 suid/guid详细解析
suid/guid 我们在前面曾经提到过s u i d和g u i d.这种权限位近年来成为一个棘手的问题.很多系统供应商不允许实现这一位,或者即使它被置位,也完全忽略它的存在,因为它会带来安全性风险 ...
- CentOS中安装JDK与Intellij idea
卸载CentOS中自带openjdk CentOS自带openjdk,可以先用java –version检测是否存在jdk版本.如果存在,最好在安装oracle的jdk之前最好卸载,可以使用如下指令 ...
- vsftp 无法启动,500 OOPS: bad bool value in config file for: anonymous_enable
朋友的FTP启动不了,叫我帮他看,启动时出现以下错误信息: 500 OOPS: bad bool value in config file for: anonymous_enable 看似配置文件错误 ...