`

线程安全

阅读更多
线程安全


一、线程安全

线程安全概念:
当多个线程访问某一个类(对象或方法)时,这个类(对象或方法)都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。

二、同一个对象的锁

1.对同一个对象的对象锁 synchronized
synchronized 锁是对象级别的

package com.study.current.thread.day01;

/**
 * 线程安全概念:当多个线程访问某一个类(对象或方法)时,这个类(对象或方法)都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
 */
public class MyThread extends Thread {

	private int count = 5 ;
	
	/**
	 * synchronized 的作用: 加锁,防止并发方法
	 * 
	 * synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。
	 */
	public synchronized void run(){
		/**
		 * 线程执行的顺序不是代码的顺序,而是CPU分配的顺序决定的
		        当过个线程访问myThread的run方法时,以排队的方式进行处理
		        排队是按照CPU分配的先后顺序而定的
		        一个线程想要执行synchronized修饰的方法里的代码,首先是尝试获得锁,如果拿到锁,执行
		   synchronized代码体内容
		        拿不到锁,这个线程就会不断地尝试获得这把锁,直到拿到为止
		        而且是多个线程同时去竞争这把锁
		 */
		this.count -- ;
		System.out.println(Thread.currentThread().getName()+" count:"+count);
	}
	
	public static void main(String[] args) {
		MyThread thread = new MyThread();
		
		Thread t1 = new Thread(thread,"t1");
		Thread t2 = new Thread(thread,"t2");
		Thread t3 = new Thread(thread,"t3");
		Thread t4 = new Thread(thread,"t4");
		Thread t5 = new Thread(thread,"t5");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
		
	}
	
}



2.例子分析

不加synchronized 运行结果:
t2 count:3
t3 count:2
t4 count:1
t1 count:3
t5 count:0

与预期的结果:count值按照顺序输出,相差很大
--------------------------------------------
添加synchronized 运行结果:
t1 count:4
t3 count:3
t2 count:2
t4 count:1
t5 count:0

count 值按照顺序输出,但t1 ~ t5 不是按照顺序,即线程执行的顺序不是代码的位置顺序
而是谁先抢到CPU的执行权,那么谁就先执行


二、多个对象的对象锁

1.对多个对象的对象锁 synchronized

多个线程多个锁:多个线程,每个线程都可以拿到自己指定的锁,分别获得锁之后,执行synchronized的方法体的内容

package com.study.current.thread.day01;

/**
 *  关键字synchronized 取得的锁都是对象锁,而不是把一段代码或方法当做锁,所以实例代码中的那个线程先执行
	synchronized关键字的方法,那个线程就持有该方法所属对象的锁Lock,两个对象,线程获得的就是两个不同的锁
	他们互不影响

	有一种情况则是相同的锁,即在静态方法上加 synchronized 关键字,表示锁定.class类,类一级别的锁,独占.class类
 *
 */
public class MultiThread extends Thread {

	public int num = 0 ; // 初始化
	
	public synchronized void printNum(String tag) throws InterruptedException{
		if("a".equals(tag)){
			num = 100 ;
			System.out.println("taga num = 100 ");
			Thread.sleep(1000);
		}else{
			num = 200 ;
			System.out.println("tagb num = 200 ");
		}
		
		System.out.println("tag : "+tag + " num : "+num);
	}
	
	public static void main(String[] args) {
		final MultiThread thread1 = new MultiThread();
		final MultiThread thread2 = new MultiThread();
		
		Thread t1 = new Thread(new Runnable() {
			
			public void run() {
				try {
					thread1.printNum("a");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			
			public void run() {
					try {
						thread2.printNum("b");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
			}
		});
		
		t1.start();
		t2.start();
	}
}




2.例子分析

运行结果:
taga num = 100
tagb num = 200
tag : b num : 200
tag : a num : 100

与预期 taga num = 100 后执行 tag : a num : 100 不同
是因为两个线程 t1 t2 分别获得了 两个不同的对象 thread1 thread2 的对象锁,互相不影响,所以运行结果与预期不同。

3.如果要保持进程间的锁
添加 class 级别的锁

package com.study.current.thread.day01;

/**
 *  关键字synchronized 取得的锁都是对象锁,而不是把一段代码或方法当做锁,所以实例代码中的那个线程先执行
	synchronized关键字的方法,那个线程就持有该方法所属对象的锁Lock,两个对象,线程获得的就是两个不同的锁
	他们互不影响

	有一种情况则是相同的锁,即在静态方法上加 synchronized 关键字,表示锁定.class类,类一级别的锁,独占.class类
 *
 */
public class MultiThread extends Thread {

	
	public static int num = 0 ; // 初始化
	
	/**
	 * 添加 static 关键字,是类级别的锁
	 * 避免不同的对象间并发执行
	 * 不同的线程获取不同的对象的锁,运行时需要相互等待
	 * 
	 * 不加 static 关键字,是对象级别的锁
	 * 不同的线程获取不同的对象的锁,互相执行不影响
	 * 
	 * @param tag
	 * @throws InterruptedException
	 */
	public static synchronized void printNum(String tag) throws InterruptedException{
		if("a".equals(tag)){
			num = 100 ;
			System.out.println("taga num = 100 ");
			Thread.sleep(1000);
		}else{
			num = 200 ;
			System.out.println("tagb num = 200 ");
		}
		
		System.out.println("tag : "+tag + " num : "+num);
	}
	
	public static void main(String[] args) {
		final MultiThread thread1 = new MultiThread();
		final MultiThread thread2 = new MultiThread();
		
		Thread t1 = new Thread(new Runnable() {
			
			public void run() {
				try {
					thread1.printNum("a");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			
			public void run() {
					try {
						thread2.printNum("b");
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
			}
		});
		
		t1.start();
		t2.start();
	}
}



运行结果:

taga num = 100
tag : a num : 100
tagb num = 200
tag : b num : 200

四:同步及异步

1.概念
同步: synchronized
同步的概念就是共享,如果不是共享的资源,就没有必要共享

异步: asynchronized
异步的概念就是独立,相互之间不受到任何制约。

同步的目的就是为了线程安全

线程安全的特性
原子性(同步)
可见性

2.列子
package com.study.current.thread.day01;

public class MyObject extends Thread {

	public synchronized void method1(){
		System.out.println(this.currentThread().getName());
		try {
			Thread.sleep(4000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	/**
	 * 	A线程先持有object 对象的Lock 锁
		B线程如果再这个时候调用对象中的同步 synchronized 方法则需等待。就是同步
		B线程可以以异步的方式调用对象中的非 synchronized 修饰的方法
			
		若method2 不加 synchronized ,则 t1 t2 同时输出,不需要获取 method2的锁
		反之,t1 输出后需等待 4 s 再输出 t2 
	 */
	public synchronized void method2(){
		System.out.println(this.currentThread().getName());
	}
	
	public static void main(String[] args) {
		
		final MyObject myO = new MyObject();
		
		Thread t1 = new Thread(new Runnable() {
			
			public void run() {
				myO.method1();
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			
			public void run() {
				myO.method2();
			}
		},"t2");
		
		t1.start();
		t2.start();
	}
}



3.分析

如果 method2 不加 synchronized 修饰
运行结果: t1  t2 同时输出
因为 method2 上无锁,不需要获取锁,即异步执行

添加收,t1 输出4s 后 t2 输出

分享到:
评论

相关推荐

    servlet线程安全问题

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

    c# 线程安全队列的用法原理及使用示例

    什么是线程安全? 答:线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等...

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

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

    CVI 线程锁、线程安全变量实例

    在计算机编程领域,尤其是涉及到实时系统和并发编程时,线程锁和线程安全变量是至关重要的概念。LabWindows/CVI是一种流行的交互式C开发环境,特别适合于开发科学和工程应用。本实例将深入探讨如何在LabWindows/CVI...

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

    但是需要注意,虽然这个方法可以保证基本的线程安全,但迭代仍然是非线程安全的,即不能在遍历过程中修改Map。 2. 使用ConcurrentHashMap:Java从1.5版本开始引入了ConcurrentHashMap,它是线程安全且高并发性能的...

    C++日志库-线程安全

    线程安全的日志库在多线程环境下尤为重要,因为不正确的日志操作可能会导致数据竞争和同步问题。本文将详细讨论如何在C++中实现一个基于Win32接口的线程安全日志库,并关注其核心概念和技术。 首先,我们需要理解...

    浅谈C#跨线程调用窗体控件(比如TextBox)引发的线程安全问题

    浅谈C#跨线程调用窗体控件引发的线程安全问题 C#跨线程调用窗体控件时可能会引发线程安全问题,例如当多个线程操作同一个控件时,该控件可能会进入不一致的状态,出现争用情况和死锁等问题。因此,确保以线程安全...

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

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

    servlet与Struts action线程安全问题分析

    Servlet和Struts Action是两种常见的Java Web开发组件,它们在多线程环境下运行时可能存在线程安全问题。线程安全是指在多线程环境中,一个类或者方法能够正确处理多个线程的并发访问,保证数据的一致性和完整性。 ...

    浅议单例模式之线程安全(转)

    在多线程环境下,线程安全的单例模式尤为重要,因为如果不正确实现,可能会导致多个线程同时创建多个实例,违反了单例模式的基本原则。 在Java中,单例模式通常有以下几种实现方式: 1. 饿汉式(静态常量): ...

    操作系统课设-线程安全的双向链表

    操作系统课程设计中实现线程安全的双向链表是一项重要的实践任务,这涉及到多线程编程、数据结构以及并发控制等核心知识点。在这个项目中,我们主要关注如何在多线程环境下构建一个能够正确操作(如插入、删除)而不...

    易语言线程安全之原子锁与读写锁

    在IT行业中,线程安全是多线程编程中的一个重要概念,确保多个线程并发执行时,数据的正确性和完整性不会受到影响。线程安全通常通过同步机制来实现,其中包括原子操作和锁机制。本文将深入探讨易语言中的原子锁与...

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

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

    java线程安全测试

    Java线程安全是多线程编程中的一个关键概念,它涉及到多个线程访问共享资源时可能出现的问题。在Java中,线程安全问题通常与并发、内存模型和可见性有关。Java内存模型(JMM)定义了如何在多线程环境下共享数据的...

    C# VS2013 收串口数据 线程安全

    在处理串口通信时,线程安全是一个关键的概念,特别是当程序需要同时处理多个任务或者从外部设备(如串口)持续接收数据时。本主题将深入探讨如何在VS2013中使用C#实现线程安全的串口数据接收。 串口通信是计算机...

    hiredis的c++封装, 线程安全

    本文将深入探讨如何使用C++进行hiredis的封装,以实现线程安全的Redis客户端操作。 首先,hiredis是Redis官方提供的一个纯C语言的简单协议解析库,它专注于处理Redis命令和响应,而忽略了更高级别的抽象。为了在C++...

    使用C++11实现线程安全的单例模式

    线程安全的单例模式在多线程环境下尤其重要,因为不正确的实现可能导致多个线程创建多个实例,这违反了单例模式的基本原则。C++11引入了新的特性,如std::mutex和std::call_once,使得实现线程安全的单例模式变得...

    Java多线程安全集合

    在Java编程中,多线程安全集合是程序员在并发环境下处理数据共享时必须考虑的关键概念。这些集合确保了在多个线程访问时的数据一致性、完整性和安全性,避免了竞态条件、死锁和其他并发问题。Java提供了一系列的线程...

    局部变量线程安全测试

    在编程领域,线程安全是多线程编程中的一个重要概念,尤其在Java、C++等支持并发编程的语言中。线程安全通常指的是当多个线程访问一个对象时,如果对象的状态始终保持一致,那么我们就说这个对象是线程安全的。这里...

Global site tag (gtag.js) - Google Analytics