`

多线程3:多线程同步

 
阅读更多

 

 

package com.test.thread;

public class ThreadTest {
	public static void main(String[] args) {
		Bank bank = new Bank();
		Thread t1 = new MoneyThread(bank);
		Thread t2 = new MoneyThread(bank);
		t1.start();
		t2.start();
	}
}

class Bank {
	int money = 1000;
	//取钱方法
	public int getMoney(int number) {
		if (number < 0) { //如果取的钱数少于0,则返回-1
			return -1;
		}
		if (number > money) {//如果取的钱数大于总数,则返回-2
			return -2;
		}
		try {
			Thread.sleep(1000); //睡一秒,模拟取款前的动作
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		money -= number;
		System.out.println("left money " + money);
		return number;
	}
}

class MoneyThread extends Thread {
	private Bank bank;
	public MoneyThread(Bank bank) {
		this.bank = bank;
	}

	public void run() {
		System.out.println(bank.getMoney(800));
	}
}

 

最后打印的结果为

left money -600
left money -600
800
800

 

 两个线程的run方法传入的都是800,至于最后为什么余额都是-600,是因为下面这行代码

money -= number;
System.out.println("left money " + money);

假设A线程执行money-=number后,此时money的值为200。B线程马上赶到,在A线程执行打印语句前执行

money -= number,此时money的值是-600,接下去两个打印语句都是打印-600。

 如果将getMoney方法改成

public synchronized int getMoney(int number) {

 加上synchronized 关键字后,打印结果为

left money 200
-2
800

 

这样就正确了。

synchronized 关键字:当synchronized 关键字修饰一个方法时,该方法就叫同步方法。

Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示该对象上锁。此时其它任何线程都无法访问该synchronized方法了,直到之前的那个线程执行方法完毕后,(或者是抛出异常),那么将该对象的锁释放掉,其它线程才有可能去访问该synchronized方法。

 

如果一个对象有多个synchronized方法,在某一时刻某个线程已进入到某个synchronized方法,那么在该方法执行完毕前,其它线程是无法访问该对象的任何synchronized方法的。

 

需额外注意的是:如果某个类中的某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在对象所对应的class对象,因为JAVA中无论一个类有多少个对象,这些对象会对应唯一一个class对象,因此当两个线程分别访问同一个类的两个对象的static synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程会先去执行方法,执行完成后,另一个线程才会开始。

 

synchronized块的写法:

synchronized(object){}

表示线程在执行时会对object对象上锁。

 

package com.test.thread;

public class ThreadTest2 {
	public static void main(String[] args) {
		Example example = new Example();
		ExampleThread1 thread1 = new ExampleThread1(example);
		ExampleThread2 thread2 = new ExampleThread2(example);
		thread1.start();
		thread2.start();
	}
}

class Example{
	public void execute(){
		synchronized (this) { //synchronized块
			for (int i = 0; i < 10; i++) {
				System.out.println("hello execute "+i);
			}
		}
	}
	public void execute2(){
		synchronized (this) { //synchronized块
			for (int i = 0; i < 10; i++) {
				System.out.println("hello execute2 "+i);
			}
		}
	} 
}

class ExampleThread1 extends Thread {
	private Example example;

	public ExampleThread1(Example example) {
		this.example = example;
	}

	public void run() {
		this.example.execute();
	}
}

class ExampleThread2 extends Thread {
	private Example example;

	public ExampleThread2(Example example) {
		this.example = example;
	}

	public void run() {
		this.example.execute2();
	}
}

打印结果为:

hello execute 0
hello execute 1
hello execute 2
hello execute 3
hello execute 4
hello execute 5
hello execute 6
hello execute 7
hello execute 8
hello execute 9
hello execute2 0
hello execute2 1
hello execute2 2
hello execute2 3
hello execute2 4
hello execute2 5
hello execute2 6
hello execute2 7
hello execute2 8
hello execute2 9

 

 

 通常建议使用synchronized块,更加细粒度。

synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法,synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内,synchronized块外的代码是可以被多个线程同时访问到的。

此外需注意的是被synchronized保护的变量应该是私有的。

 

 

 

 

package com.test.thread;

/*
 * 此类实现在0,1之间切换
 */
public class Sample {
	private int number = 0;

	/**
	 * 当number值为0时加1,否则等待 
	 */
	public synchronized void increase() {
		while (0 != number) {
			try {
				wait();//等待,释放资源
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		number++;
		System.out.println(number);
		notify();  //唤醒对象
	}

	/**
	 * 当number值为1时减1,否则等待 
	 */
	public synchronized void decrease() {
		while (0 == number){
			try{
				wait();
			}catch (Exception e) {
				e.printStackTrace();
			}
		}
		number --;
		System.out.println(number);
		notify();
	}
}

 

package com.test.thread;

public class IncreaseThread extends Thread {
	private Sample sample;

	public IncreaseThread(Sample sample) {
		this.sample = sample;
	}

	public void run() {
		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep((long)(Math.random() * 1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			sample.increase();
		}
	}
}

 

 

package com.test.thread;

public class DecreaseThread extends Thread{
	private Sample sample;

	public DecreaseThread(Sample sample) {
		this.sample = sample;
	}

	public void run() {
		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep((long)(Math.random() * 1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			sample.decrease();
		}
	}
}

 

package com.test.thread;

public class MainTest {
	public static void main(String[] args) {
		Sample sample = new Sample();
		IncreaseThread t1 = new IncreaseThread(sample);
		DecreaseThread t2 = new DecreaseThread(sample);
		IncreaseThread t3 = new IncreaseThread(sample);
		DecreaseThread t4 = new DecreaseThread(sample);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

 

上例有两个线程类:分别是IncreaseThread和DecreaseThread,在MainTest中生成了四个线程,用于同步访问Sample类,最后打印的结果为1,0.......

 

分享到:
评论

相关推荐

    多线程同步:事件(event)

    在给定的“多线程同步:事件(event)”主题中,我们将深入探讨如何利用事件来实现线程间的同步,以及如何控制线程访问资源的顺序。 首先,理解线程同步的概念至关重要。当多个线程试图同时访问和修改同一资源时,...

    多线程代码 经典线程同步互斥问题 生产者消费者问题

    c: 多线程访问同一资源 d: 经典线程同步互斥问题 e: 使用关键段解决子线程互斥问题 f: 利用事件实现线程同步问题 g: 利用互斥量来解决线程同步互斥问题 h: problem1 生产者消费者问题 (1生产者 1消费者 1...

    C语言多线程编程:线程控制与同步机制详解

    内容概要:本文详细介绍了C语言中的多线程编程及其线程控制与同步机制。文章首先阐述了多线程编程的重要性及其在现代计算环境中的应用背景。随后,重点讲解了C语言中多线程编程的基础,包括使用POSIX线程库...

    3种多线程实现同步方法

    本篇文章将深入探讨三种在C++中实现多线程同步的方法:事件对象、关键代码段和互斥对象。 首先,我们来看**事件对象**。事件对象是一种信号机制,用于线程间通信和同步。在Windows API中,CreateEvent函数创建一个...

    Linux多线程服务端编程:使用muduo C++网络库

    掌握两种基本的同步原语就可以满足各种多线程同步的功能需求,还能写出更易用的同步设施。掌握一种进程间通信方式和一种多线程网络编程模型就足以应对日常开发任务,编写运行于公司内网环境的分布式服务统。

    使用三种VC的多线程同步方法编写一个多线程的程序

    1.使用三种VC的多线程同步方法编写一个多线程的程序(要求在屏幕上先显示Hello,再显示World)。 1)基于全局变量的多线程同步程序; 2)基于事件的多线程同步程序;...3)基于临界区的多线程同步程序。

    Java多线程同步.pdf

    "Java多线程同步.pdf" Java多线程同步是指在Java语言中,如何使用synchronized关键字和其他同步机制来确保多线程程序的正确执行。在Java语言中,synchronized关键字用于对方法或者代码块进行同步,但是仅仅使用...

    MFC 多线程及线程同步

    MFC 多线程及线程同步 MFC 多线程及线程同步 MFC 多线程及线程同步

    Delphi多线程同步的例子

    3. **线程排序示例(SortThreads)**:这个示例可能展示了一个使用多线程进行数据排序的场景。通常,大型数据集的排序可以分割成多个小任务,每个任务在一个独立的线程中执行,最后再将结果合并。这样可以大大提高...

    创建多线程线程同步

    本主题将深入探讨如何在编程中创建多线程以及如何实现线程同步,特别关注CEVENT对象在Windows API中的应用。 **一、多线程的基本概念** 多线程是指在一个进程中可以同时运行多个独立的执行流,每个执行流被称为一...

    多线程的运用e语言多线程 e多线程

    3. 资源复用:多线程共享进程资源,减少内存开销。 缺点: 1. 线程安全:线程间的数据共享可能导致数据竞争和死锁。 2. 调度开销:线程的创建、销毁和上下文切换都有一定的系统开销。 3. 资源限制:过多的线程可能...

    多线程同步:事件,代码

    很简单的一个事件同步代码,本人也是初学C++,加油啦!

    java多线程同步问题

    多线程注意:wait()方法的调用要有判定条件常用 while () obj.wait(timeout, nanos); ... // Perform action appropriate to condition } synchronized会影响共享数据,但对其他语句的执行不会有规律了!

    易语言源码多线程类源码.rar

    3. 线程同步:在多线程环境下,为了防止数据竞争和死锁,需要进行线程同步。易语言提供了多种同步机制,如信号量、互斥量和事件等,用于控制线程间的执行顺序和资源访问。例如,互斥量可以确保同一时间只有一个线程...

    VC++多线程同步基本示例

    本示例着重讲解了VC++中的多线程同步,这是多线程编程中确保数据安全和正确性的重要概念。我们将深入探讨临界区、互斥量、事件和信号量这四种多线程同步机制。 1. **临界区(Critical Section)**:临界区是多线程...

    C# 多线程实例多线程实例多线程实例

    在编程领域,多线程是实现并发执行任务的关键技术,特别是在C#这样的语言中,它提供了丰富的多线程支持。本文将深入探讨C#中的多线程实例,以帮助开发者理解如何有效地利用多核处理器资源,提高程序的执行效率。 多...

    C语言多线程编程:并行开发的技术与实践

    然而,多线程编程也引入了复杂的同步问题,需要开发者谨慎处理。通过遵循最佳实践和使用适当的同步机制,可以有效地避免多线程编程中常见的问题,编写出高效、稳定的并行程序。随着多核处理器的普及,掌握多线程编程...

    多线程不同步读写共享资源代码

    满足这两个条件,就可以不用进行线程同步啦! 如何保证读在写之后呢,方法有多种,比如 读线程判断条件if(读指针 == 写指针)。写线程判断条件 if(下一个指针==读线程指针) 其实这就是一简单的生产者与消费者问题。 ...

    c++实现多线程同步

    通过分析和学习这个示例,你可以更好地理解如何在Windows环境下用C++实现多线程同步。 总之,C++的多线程功能使得开发者能够充分利用现代硬件的并行处理能力,而信号量作为一种有效的同步工具,可以防止数据竞争,...

    C#实现多线程同步并发操作

    本文将深入探讨如何在C#中实现多线程同步并发操作,这不仅对于提高软件性能至关重要,也是高级程序员必须掌握的核心技能之一。 ### C#中的多线程同步并发操作 多线程编程可以极大地提高CPU的利用率,特别是在处理I...

Global site tag (gtag.js) - Google Analytics