一.线程不安全示例:
代码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(){.....}
分享到:
相关推荐
本文将深入探讨在多线程环境中使用List时遇到的非线程安全问题,并提供相应的解决方案和最佳实践。 List是.NET框架中常用的一个动态数组,它提供了方便的增删改查操作。然而,List并未设计为线程安全的容器,这意味...
### Delphi多线程的安全问题分析及解决 #### 摘要 本文深入探讨了Delphi环境下多线程运行过程中可能遇到的安全问题及其解决方案。在Windows操作系统中,多线程技术因其高效性和灵活性而被广泛应用于软件开发之中。...
Servlet 线程安全问题是指在使用 Servlet 编程时,如果不注意多线程安全性问题,可能会导致难以发现的错误。Servlet/JSP 技术由于其多线程运行而具有很高的执行效率,但这也意味着需要非常细致地考虑多线程的安全性...
4. 避免在多线程环境中直接使用HashMap:如果你确定不需要在多线程环境下共享HashMap,那么可以考虑局部变量的方式,只在单个线程中使用HashMap,这样就无需担心线程安全问题。 总结起来,理解HashMap的线程不安全...
在IT行业中,尤其是在开发高并发应用时,线程安全是一个至关重要的问题。"C# 高效线程安全,解决多线程写txt日志类.zip" 提供了一个专门用于多线程环境下写入txt日志文件的解决方案,确保了在并发写入时的数据一致性...
8. **并发集合**:C#提供了线程安全的集合,如`ConcurrentQueue`、`ConcurrentStack`和`ConcurrentDictionary`,它们在多线程环境下保证数据一致性。模板可能利用这些集合来安全地共享数据。 9. **性能监控**:在多...
总的来说,这个压缩包提供了一个用于VB6的多线程ActiveX组件,包括了实现多线程安全操作的关键库(MT32.dll)、一个演示示例(Demo.exe和frmMain.frm)以及相关的文档(MT32.rtf)。对于那些在VB6环境下开发多线程...
在C#编程中,线程安全是多线程应用程序中至关重要的一个方面,尤其是在处理共享资源如文本日志文件时。本主题将深入探讨如何在C#中创建一个高效的线程安全日志类,用于在多线程环境中安全地写入txt日志。 首先,...
3. **线程安全**:在多线程环境中,对SQLite的读写操作需要确保线程安全。否则,多个线程同时访问数据库可能导致数据损坏或不一致。C#中的`lock`关键字是一种同步机制,可以防止多个线程同时访问共享资源。 4. **...
然而,当在多线程环境中使用SQLite时,需要注意一些关键问题以确保数据的安全性和一致性。以下是四个重要的考虑因素: 1. **线程安全**: SQLite本身并不提供完全的线程安全,这意味着在不同线程中并发访问数据库...
在VB6中,由于其默认的单线程 Apartment (STA) 模型,直接在控件中实现多线程可能会遇到各种问题,如数据同步、线程安全和资源竞争等。这个组件MT32.dll,可能是解决这些问题的关键,它的版本升级至1.0.0.0,表明该...
"gethostbyname_r在某些linux版本中多线程不安全问题" 在 Linux 操作系统中,gethostbyname_r 函数是一个常用的 DNS 解析函数,但是,在某些 Linux 版本中,这个函数存在多线程不安全问题。本文将详细介绍这个问题...
标题中的"多线程安全dictionary"指的是在多线程环境下能够确保数据一致性、避免竞态条件的字典类。下面将详细讨论几种实现多线程安全的字典类型及其特点: 1. **ConcurrentDictionary, TValue>** - `System....
mysql是线程不安全的,mysql不是线程安全的,多线程共用同一个mysql连接是会崩溃的 QT的QSqlDatabase是基于mysql的,所以一样是线程不安全的 现讲明mysql为什么是线程不安全的,以及在多线程环境下如何使用mysql,...
总的来说,多线程编程是提升软件性能和响应能力的重要手段,学习如何有效利用多线程,不仅可以提高程序的并发性,还能解决复杂的问题,如异步I/O、并行计算等。通过不断实践和学习,你将在多线程的世界里游刃有余。
"鱼刺框架"的稳定特性意味着它在设计时考虑了线程安全性和资源管理,确保在多线程环境下不会出现竞态条件、死锁或其他常见的并发问题。这些特性通常包括锁机制(如互斥量、读写锁)、信号量、条件变量等同步原语,...
但在多线程环境中,由于线程间的并发执行,直接传递可能会引发数据不一致的问题。易语言提供了一种安全的方式来传递参数,即通过“线程参数”数据结构。创建线程时,我们可以将需要传递的数据封装到线程参数中,确保...
在这个"多线程互斥实例 多线程获取同一变量"的示例中,我们将探讨如何在多个线程中安全地访问共享资源,避免数据不一致性和竞态条件。 首先,我们需要理解多线程中的一些核心概念: 1. **线程**:线程是操作系统...
本篇文章将深入探讨“最简单的线程安全问题”,并结合相关源码和工具来帮助理解。线程安全问题通常涉及到多个线程对共享资源的访问,如果管理不当,可能会导致数据不一致、死锁等问题。 首先,我们需要了解什么是...