`
高军威
  • 浏览: 182239 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

多线程(多线程的安全问题)

阅读更多
一.线程不安全示例:
代码1:
class Ticket implements Runnable
{
	public int tick = 100;
	Object obj = new Object();
	boolean flag = true;
	@Override
	public void run() {
		while (true) {
			if(tick>0)
			{
				try { Thread.sleep(10); }
				catch (InterruptedException e) 
				{ e.printStackTrace(); }
				System.out.println(Thread.currentThread().getName()+"...sale: "+tick--);
			}else {
				break;
			}
		}
	}
}

class TicketDemo2{
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		
		Thread t1 = new Thread(ticket,"张三");
		Thread t2 = new Thread(ticket,"李四");
		Thread t3 = new Thread(ticket,"王五");
		
		t1.start();
		t2.start();
		t3.start();
	}
}

结果出现数据错误:


错误原因:
当多条语句在操作同一个线程共享数据是,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

1.将TicketDemo2改为:
代码2:
class TicketDemo2{
	public static void main(String[] args) {
		Ticket ticket1 = new Ticket();
		Ticket ticket2 = new Ticket();
		Ticket ticket3 = new Ticket();
		
		Thread t1 = new Thread(ticket1,"张三");
		Thread t2 = new Thread(ticket2,"李四");
		Thread t3 = new Thread(ticket3,"王五");
		
		t1.start();
		t2.start();
		t3.start();
	}
}

结果没有出现数据错误:


原因:代码1中3个线程对同一个ticket对象操作(多线程),ticket是共享数据,多线程环境中容易发生安全问题;
代码2中是3线程不是对同一个ticket对象操作,线程t1、t2、t3分别对不同的ticket对象操作,3个ticket对象不是指向同一个内存地址,所以t1、t2、t3不存在共享数据,所以在此例中不会出现线程安全问题。

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

Java对于多线程的安全问题提供了专业的解决方式。
同步代码块:
synchronized (obj) {

}


解决代码1中的不安全问题:
class Ticket implements Runnable
{
	public int tick = 100;
	Object obj = new Object();
	boolean flag = true;
	@Override
	public void run() {
		while (true) {
			synchronized (obj) {
				if(tick>0)
				{
					try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
					System.out.println(Thread.currentThread().getName()+"...sale: "+tick--);
				}else {
					break;
				}
			}
		}
	}
}

class TicketDemo2{
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		
		Thread t1 = new Thread(ticket);
		Thread t2 = new Thread(ticket);
		Thread t3 = new Thread(ticket);
		
		t1.start();
		t2.start();
		t3.start();
	}
}


2.同步函数
/**
同步函数使用的this函数
*/
class Ticket implements Runnable
{
	public int tick = 100;
	Object obj = new Object();
	boolean flag = true;
	@Override
	public synchronized void run() {
		while (true) {
			show();
		}
	}
	public synchronized void show()
	{
		if(tick>0)
		{
			try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
			System.out.println(Thread.currentThread().getName()+"...sale: "+tick--);
		}
	}
}


通俗解释:火车上的卫生间---经典
同步可以解决线程的安全问题;

同步的前提:
a.必须要有两个或两个以上的线程。
b.必须是多个线程使用同一个锁。

必须保证同步中只能有一个线程运行。
好处:解决多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源

有的需要同步,有的不需要同步,同步范围过大导致程序性能差;

列子2: 存钱——————————————————————————————————

二.线程不安全 :
代码3:
package ticket03;
/**
 * 需求:
 * 银行有一个金库。
 * 有两个储户分别寸300元,每次寸100,寸3次。
 * 
 * */
class Bank 
{
	private int sum;
	public void add(int n) {
		sum = sum + n;
		System.out.println("sum="+sum);
	}
}
class Cus implements Runnable
{
	private Bank b = new Bank();
	@Override
	public void run() {
		for (int i=0;i<3;i++) {
			b.add(100);
		}
	}
}

class BankDemo{
	public static void main(String[] args) {
		Cus c = new Cus();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();
	}
}

运行结果:


解决代码3中的线程不安全问题:
代码4:同步共享数据
class Bank 
{
	private int sum;
	Object obj = new Object();
	public void add(int n) {
		synchronized(obj)
		{
			sum = sum + n;
			try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
			System.out.println("sum="+sum);
		}
	}
}

运行结果:


代码5:同步this
class Bank 
{
	private int sum;
	//Object obj = new Object();
	public void add(int n) {
		synchronized(this)
		{
			sum = sum + n;
			try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
			System.out.println(Thread.currentThread().getName()+"---sum="+sum);
		}
	}
}


运行结果:


代码6:同步函数
class Bank 
{
	private int sum;
	public synchronized void add(int n) {
		sum = sum + n;
		try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
		System.out.println(Thread.currentThread().getName()+"---sum="+sum);
	}
}

运行结果:


证实同步函数使用的锁是 this
代码7:
package ticket02;
/**
 * 同步函数用的是那一个锁呢?
 * 函数需要被对象调用,那么函数都有一个所属对象引用,就是this。
 * 所以同步函数使用的锁是 this。
 * 
 * 通过改程序进行验证。
 * 
 * 使用两个线程来买票。
 * 一个线程在同步代码块中,
 * 一个线程在同步函数中。
 * 
 * 都在执行买票动作
 * */
class Ticket implements Runnable
{
	public int tick = 100;
	Object obj = new Object();
	boolean flag = true;
	@Override
	public void run() {
		if(flag)
		{
			while (true) {
				synchronized (obj) {
					if(tick>0)
					{
						try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
						System.out.println(Thread.currentThread().getName()+"...sale: "+tick--);
					}else {
						break;
					}
				}
			}
		}else {
			while (true)
				show();
		}
		
	}
	public synchronized void show()
	{
		if(tick>0)
		{
			try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
			System.out.println(Thread.currentThread().getName()+"...show...: "+tick--);
		}
	}
}

class TicketDemo2{
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		
		Thread t1 = new Thread(ticket);
		Thread t2 = new Thread(ticket);
		
		t1.start();
		try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
		ticket.flag=false;
		t2.start();
	}
}


运行结果:


分析原因:加了同步还出现问题,就是同步前提出问题了:
同步的前提:
1.必须要有两个或两个以上的线程。(满足)
2.必须是多个线程使用同一个锁。(不是)
一个是obj锁,一个是this锁。
验证下,将obj改为this测试:



这次就没问题了。

3.如果同步函数被静态修饰后,使用的锁是什么呢?
代码8:
package ticket02;
/**
 * 如果同步函数被静态修饰后,使用的锁是什么呢?
 * 通过验证,发现不在是this。因为静态方法中也不可以定义this。
 *
 * 静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。
 * 类名.class,该对象的类型是 class
 * 
 * 静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class。
 * */
class Ticket implements Runnable
{
	public static int tick = 100;
	Object obj = new Object();
	boolean flag = true;
	@Override
	public void run() {
		if(flag)
		{
			while (true) {
				synchronized (this) {
					if(tick>0)
					{
						try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
						System.out.println(Thread.currentThread().getName()+"...sale: "+tick--);
					}else {
						break;
					}
				}
			}
		}else {
			while (true)
				show();
		}
		
	}
	public static synchronized void show()
	{
		if(tick>0)
		{
			try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
			System.out.println(Thread.currentThread().getName()+"...show...: "+tick--);
		}
	}
}

class TicketDemo2{
	public static void main(String[] args) {
		Ticket ticket = new Ticket();
		
		Thread t1 = new Thread(ticket);
		Thread t2 = new Thread(ticket);
		
		t1.start();
		try {Thread.sleep(10);}catch (InterruptedException e){ e.printStackTrace(); }
		ticket.flag=false;
		t2.start();
	}
}


运行结果:线程不安全


总结:静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class。
synchronized(this){ }等价于publicsynchronized void method(){.....}
分享到:
评论

相关推荐

    C#多线程List的非线程安全性

    本文将深入探讨在多线程环境中使用List时遇到的非线程安全问题,并提供相应的解决方案和最佳实践。 List是.NET框架中常用的一个动态数组,它提供了方便的增删改查操作。然而,List并未设计为线程安全的容器,这意味...

    Delphi多线程的安全问题分析及解决

    ### Delphi多线程的安全问题分析及解决 #### 摘要 本文深入探讨了Delphi环境下多线程运行过程中可能遇到的安全问题及其解决方案。在Windows操作系统中,多线程技术因其高效性和灵活性而被广泛应用于软件开发之中。...

    servlet线程安全问题

    Servlet 线程安全问题是指在使用 Servlet 编程时,如果不注意多线程安全性问题,可能会导致难以发现的错误。Servlet/JSP 技术由于其多线程运行而具有很高的执行效率,但这也意味着需要非常细致地考虑多线程的安全性...

    关于如何解决HashMap线程安全问题的介绍

    4. 避免在多线程环境中直接使用HashMap:如果你确定不需要在多线程环境下共享HashMap,那么可以考虑局部变量的方式,只在单个线程中使用HashMap,这样就无需担心线程安全问题。 总结起来,理解HashMap的线程不安全...

    C# 高效线程安全,解决多线程写txt日志类.zip

    在IT行业中,尤其是在开发高并发应用时,线程安全是一个至关重要的问题。"C# 高效线程安全,解决多线程写txt日志类.zip" 提供了一个专门用于多线程环境下写入txt日志文件的解决方案,确保了在并发写入时的数据一致性...

    大漠多线程模板_大漠_大漠多线程_

    8. **并发集合**:C#提供了线程安全的集合,如`ConcurrentQueue`、`ConcurrentStack`和`ConcurrentDictionary`,它们在多线程环境下保证数据一致性。模板可能利用这些集合来安全地共享数据。 9. **性能监控**:在多...

    vb6实现安全多线程的ActiveX组件

    总的来说,这个压缩包提供了一个用于VB6的多线程ActiveX组件,包括了实现多线程安全操作的关键库(MT32.dll)、一个演示示例(Demo.exe和frmMain.frm)以及相关的文档(MT32.rtf)。对于那些在VB6环境下开发多线程...

    C# 高效线程安全,解决多线程写txt日志类

    在C#编程中,线程安全是多线程应用程序中至关重要的一个方面,尤其是在处理共享资源如文本日志文件时。本主题将深入探讨如何在C#中创建一个高效的线程安全日志类,用于在多线程环境中安全地写入txt日志。 首先,...

    C#多线程读写sqlite

    3. **线程安全**:在多线程环境中,对SQLite的读写操作需要确保线程安全。否则,多个线程同时访问数据库可能导致数据损坏或不一致。C#中的`lock`关键字是一种同步机制,可以防止多个线程同时访问共享资源。 4. **...

    QT中sqlite多线程操作4个注意问题

    然而,当在多线程环境中使用SQLite时,需要注意一些关键问题以确保数据的安全性和一致性。以下是四个重要的考虑因素: 1. **线程安全**: SQLite本身并不提供完全的线程安全,这意味着在不同线程中并发访问数据库...

    vb6实现安全多线程的ActiveX组件(1.0.0.0)

    在VB6中,由于其默认的单线程 Apartment (STA) 模型,直接在控件中实现多线程可能会遇到各种问题,如数据同步、线程安全和资源竞争等。这个组件MT32.dll,可能是解决这些问题的关键,它的版本升级至1.0.0.0,表明该...

    gethostbyname_r在某些linux版本中多线程不安全问题.pdf

    "gethostbyname_r在某些linux版本中多线程不安全问题" 在 Linux 操作系统中,gethostbyname_r 函数是一个常用的 DNS 解析函数,但是,在某些 Linux 版本中,这个函数存在多线程不安全问题。本文将详细介绍这个问题...

    多线程安全dictionary

    标题中的"多线程安全dictionary"指的是在多线程环境下能够确保数据一致性、避免竞态条件的字典类。下面将详细讨论几种实现多线程安全的字典类型及其特点: 1. **ConcurrentDictionary, TValue&gt;** - `System....

    mysql是线程不安全的,mysql不是线程安全的,多线程共用同一个mysql连接是会崩溃的.所以同样QT的QSqlDatabase也是线程不安全的,QS会崩溃

    mysql是线程不安全的,mysql不是线程安全的,多线程共用同一个mysql连接是会崩溃的 QT的QSqlDatabase是基于mysql的,所以一样是线程不安全的 现讲明mysql为什么是线程不安全的,以及在多线程环境下如何使用mysql,...

    多线程编程示例

    总的来说,多线程编程是提升软件性能和响应能力的重要手段,学习如何有效利用多线程,不仅可以提高程序的并发性,还能解决复杂的问题,如异步I/O、并行计算等。通过不断实践和学习,你将在多线程的世界里游刃有余。

    鱼刺多线程注册源码例子(鱼刺多线程稳定框架)

    "鱼刺框架"的稳定特性意味着它在设计时考虑了线程安全性和资源管理,确保在多线程环境下不会出现竞态条件、死锁或其他常见的并发问题。这些特性通常包括锁机制(如互斥量、读写锁)、信号量、条件变量等同步原语,...

    易语言多线程传递多参数

    但在多线程环境中,由于线程间的并发执行,直接传递可能会引发数据不一致的问题。易语言提供了一种安全的方式来传递参数,即通过“线程参数”数据结构。创建线程时,我们可以将需要传递的数据封装到线程参数中,确保...

    C#多线程互斥实例 多线程获取同一变量

    在这个"多线程互斥实例 多线程获取同一变量"的示例中,我们将探讨如何在多个线程中安全地访问共享资源,避免数据不一致性和竞态条件。 首先,我们需要理解多线程中的一些核心概念: 1. **线程**:线程是操作系统...

    Java多线程 - (一) 最简单的线程安全问题

    本篇文章将深入探讨“最简单的线程安全问题”,并结合相关源码和工具来帮助理解。线程安全问题通常涉及到多个线程对共享资源的访问,如果管理不当,可能会导致数据不一致、死锁等问题。 首先,我们需要了解什么是...

Global site tag (gtag.js) - Google Analytics