多任务编程的难点在于多任务共享资源。对于同一个进程空间中的多个线程来说,它们都共享堆中的对象。某个线程对对象的操作,将影响到其它的线程。

在多线程编程中,要尽力避免竞争条件(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 同步的更多相关文章

  1. java 多线程并发 synchronized 同步机制及方式

    2. 锁机制 3. 并发 Excutor框架 4. 并发性与多线程介绍 1. synchronized  参考1. synchronized 分两种方式进行线程的同步:同步块.同步方法 1. 方法同步 ...

  2. 四、java多线程核心技术——synchronized同步方法与synchronized同步快

    一.synchronized同步方法 论:"线程安全"与"非线程安全"是多线程的经典问题.synchronized()方法就是解决非线程安全的. 1.方法内的变 ...

  3. 关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇高质量的博文)

    Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享 ...

  4. Java多线程-线程的同步(同步方法)

    线程的同步是保证多线程安全访问竞争资源的一种手段.线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些 ...

  5. java多线程:线程同步synchronized(不同步的问题、队列与锁),死锁的产生和解决

    0.不同步的问题 并发的线程不安全问题: 多个线程同时操作同一个对象,如果控制不好,就会产生问题,叫做线程不安全. 我们来看三个比较经典的案例来说明线程不安全的问题. 0.1 订票问题 例如前面说过的 ...

  6. Java多线程 3 线程同步

    在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系.可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题.现在就来学习多线程对数据访问的 ...

  7. Java多线程-线程的同步与锁

    一.同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏.例如:两个线程ThreadA.ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据. package ...

  8. java中的synchronized同步代码块和同步方法的区别

    下面这两段代码有什么区别? //下列两个方法有什么区别 public synchronized void method1(){} public void method2(){ synchronized ...

  9. Java多线程与线程同步

    六.多线程,线程,同步 ①概念: 并行:指两个或多个在时间同一时刻发生(同时发生) 并发:指两个或多个事件在同一时间段内发生 具体概念: 在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多 ...

随机推荐

  1. UNIX网络编程读书笔记:名字与地址转换

    概述 在名字和数值地址间进行转换的函数: gethostbyname和gethostbyaddr:在主机名字与IPv4地址之间进行转换.仅仅支持IPv4. getservbyname和getservb ...

  2. Python break

    break退出循环 用 for 循环或者 while 循环时,如果要在循环体内直接退出循环,可以使用 break 语句. 比如计算1至100的整数和,我们用while来实现: sum = 0 x = ...

  3. 让Sql Server 2008 可以远程连接的方法

    1.先开防火墙TCP:1433 2.设置外围端口为:1433 注意一定不要忘记“启用”和IPALL的端口设置 3.重启SQL完成 如果要查看1433端口有没有启用并被监听,只要在cmd里使用netst ...

  4. ibatis 批量插入

      ibatis 批量插入 CreationTime--2018年7月2日10点21分 Author:Marydon 1.说明 基于oracle的sql语句 2.主键id有默认值,比如:sys_gui ...

  5. 【Oracle】查看正在运行的存储过程

    select name from v$db_object_cache where locks > 0 and pins > 0 and type='PROCEDURE';

  6. 逻辑回归的相关问题及java实现

    本讲主要说下逻辑回归的相关问题和详细的实现方法 1. 什么是逻辑回归 逻辑回归是线性回归的一种,那么什么是回归,什么是线性回归 回归指的是公式已知,对公式中的未知參数进行预计,注意公式必须是已知的,否 ...

  7. python --特殊方法与多范式

    转自:http://www.cnblogs.com/vamei/archive/2012/11/19/2772441.html Python一切皆对象,但同时,Python还是一个多范式语言(mult ...

  8. mysql group replication 安装&配置详解

    一.原起: 之前也有写过mysql-group-replication (mgr) 相关的文章.那时也没有什么特别的动力要写好它.主要是因为在 mysql-5.7.20 之前的版本的mgr都有着各种各 ...

  9. CentOS 6.4 yum安装chrome

    CentOS 6.4安装chrome浏览器 vim /etc/yum.repos.d/CentOS-Base.repo 根据你的系统增加一个节点 32-bit [google] name=Google ...

  10. Python中模拟C# Linq的一些操作

    闲来无事时积累的一些Linq函数,有时间就更新. 需要注意python版本如果低于3.0不支持lambda,只能单独写函数传参,比较麻烦 1.FirstOrDefault: def FirstOrDe ...