`
落花残月
  • 浏览: 5304 次
  • 性别: Icon_minigender_1
  • 来自: 舟山
社区版块
存档分类
最新评论

Java多线程并发锁和原子操作,你真的了解吗?

阅读更多

 

对于Java多线程,接触最多的莫过于使用synchronized,这个简单易懂,可谓一招吃遍天下,可能在一些特殊场景下确需要用一些其他的同步进行。今天我就简单介绍一下几种锁,其实像类似的博客和文章和很多,所以也不会多赘述,我更想抛出在实际应用中所碰到的问题。好,废话少说。

volatile

作为Java中的轻量级锁,当多线程中一个线程操作后可以保证其他线程可见,也就是书上所说的“可见性”,另外一个就是“重排序”。所谓重排序指的是JVM对指令的优化。很多人可能在实际实验中发现好像不是如此,最后的例子我也会说明这一点。

 

synchronized

这个作为Java中“重量级”的线程安全机制被大家所熟知。有些Java开发可能不知道volatile,也不太会去使用。但是如果不知道synchronized,估计这种情况就比较少了。

 

java.util.concurrent.locks.ReentrantLock

java.util.concurrent.中是JDK1.5中出的对于一些并发操作的类库,其中包括很多同学很喜欢的原子类,比如说AtomicInteger,我在这里也不会对其原理做过多的分析,因为我也还没有去看其源代码,所以也不好讲,也不是今天我想要讲的重点。

好,说一下ReentrantLock,这个锁主要是能显示的添加锁和释放锁,好处是更加灵活,能够更加准确的控制锁,也能确保系统的稳定,比如说“重连”。后面代码会有使用到。

 

项目中如果没有特殊需求除了synchronized其他也比较少用,各位编程爱好者可以在自己的项目中多使用其他锁机制以了解其工作原理和优劣。技术一定会精进不少。

实际场景

 

package com.yuzhipeng.m07.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 
 * @author Yu zhipeng | 2012-07-26
 *
 */
public class TestMultiThread  implements Runnable{

	private static int i;
	
	private static volatile Integer vi = 0;
	
	private static AtomicInteger ai = new AtomicInteger();
	
	private static Integer si = 0;
	
	private static int ri;
	
	private static AtomicInteger flag = new AtomicInteger();
	
	private Lock lock = new ReentrantLock();
	
	@Override
	public void run() {
		for(int k=0;k<200000;k++){
			i++;
			vi++;
			ai.incrementAndGet();
			synchronized(si){
				si++;
			}
			lock.lock();
			try{
				ri++;
			}finally{
				lock.unlock();
			}
			
		}
		flag.incrementAndGet();
	}
	
	public static void main(String[] args) throws InterruptedException{
		TestMultiThread t1 = new TestMultiThread();
		TestMultiThread t2 = new TestMultiThread();
		ExecutorService exec1 = Executors.newCachedThreadPool();
		ExecutorService exec2 = Executors.newCachedThreadPool();
		exec1.execute(t1);
		exec2.execute(t2);
		while(true){
			if(flag.intValue()==2){
				System.out.println("i>>>>>"+i);
				System.out.println("vi>>>>>"+vi);
				System.out.println("ai>>>>>"+ai);
				System.out.println("si>>>>>"+si);	
				System.out.println("ri>>>>>"+ri);	
				break;
			}
			Thread.sleep(50);
		}

		
	}
	
}

输出结果:

i>>>>>381890
vi>>>>>353610
ai>>>>>400000
si>>>>>392718
ri>>>>>392658

从上面的输出结果来看真是让人大感意外,只有原子操作AtomicInteger的结果保证了多线程的安全性,而其他不管是用轻量级的volatile还是重量级的synchronized都没有达到我们想要的效果。这也让我产生了大在的怀疑。难道问题真的这么蹊跷?

从这里不难看出除了AtomicInteger用的是其自己的方法而其他都是用到了Java的语法糖++操作。而这让我想起了++操作并非原子操作,而可能在其中间操作导致了其他线程对其他进行了修改,虽然同样的问题我在《Think in Java》中也找到可以佐证的例子。可是我在这里始终没有办法想通,因为我对si已经加了synchronized操作。难道还会有问题?这让我想把这段代码编译成字节码的冲动。好吧,下面看字节码(这里我单独把synchronized这一段操作抽出来,作为分析,其他几个就算了,不然编译后的字节码有点多)

为了方便看,先贴出源代码

public class TestSynchronizedThread  implements Runnable{

	private static Integer si = 0;
	
	@Override
	public void run() {
		for(int k=0;k<200000;k++){
			synchronized(si){
				si++;
			}
		}
	}
}


下面是字节码,为了节省篇幅,一些不重要的部分我将不贴出

   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   ldc     #2; //int 200000
   5:   if_icmpge       55
   8:   getstatic       #3; //Field si:Ljava/lang/Integer;
   11:  dup
   12:  astore_2
   13:  monitorenter
   14:  getstatic       #3; //Field si:Ljava/lang/Integer;
   17:  astore_3
   18:  getstatic       #3; //Field si:Ljava/lang/Integer;
   21:  invokevirtual   #4; //Method java/lang/Integer.intValue:()I
   24:  iconst_1
   25:  iadd
   26:  invokestatic    #5; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   29:  dup
   30:  putstatic       #3; //Field si:Ljava/lang/Integer;
   33:  astore  4
   35:  aload_3
   36:  pop
   37:  aload_2
   38:  monitorexit
   39:  goto    49
   42:  astore  5
   44:  aload_2
   45:  monitorexit
   46:  aload   5
   48:  athrow
   49:  iinc    1, 1
   52:  goto    2
   55:  return

从这里一看从monitorenter进入安全区到monitorexit出安全区没有发现si是处于中间状态的,那又是在哪出的问题呢?我已经无法解释了!!还请各位大侠指点。

 

PS:

这个问题肯定出在++操作上,如果我把锁对象换成其他公用对象,那这个问题得到解决。

 

 

分享到:
评论

相关推荐

    java 多线程并发实例

    在Java编程中,多线程并发是提升程序执行效率、充分利用多核处理器资源的重要手段。本文将基于"java 多线程并发实例"这个主题,深入探讨Java中的多线程并发概念及其应用。 首先,我们要了解Java中的线程。线程是...

    java多线程中的原子操作

    在Java多线程编程中,原子操作是一种非常关键的概念,它涉及到并发控制和线程安全。原子操作是指在不被其他线程中断的情况下,能够完整执行的一个或一系列操作。这样的操作在多线程环境中可以保证数据的一致性和完整...

    JAVA并发多线程的面试问题及答案-java多线程并发面试题.docx

    原子操作是 Java 多线程并发编程中非常重要的一方面。面试官通常会问一些相关的问题,以检测候选者的基础知识和实际编程能力。 * 什么是原子操作,Java 中的原子操作是什么? 这个问题是检测候选者对原子操作的理解...

    java多线程、锁的教程跟案例

    Java多线程与锁是Java并发编程中的核心概念,它们对于构建高效、可扩展的并发应用程序至关重要。在Java中,多线程允许程序同时执行多个任务,提高CPU的利用率,而锁则是用来控制多线程间共享资源的访问,确保数据的...

    通过多线程编程在Java中发现的并发模式和特性——线程、锁、原子等.zip

    在Java编程中,多线程是处理并发执行的关键技术,它允许程序同时执行多个任务,提高了系统的效率和响应性。本资料主要探讨了Java中的并发模式和特性,涉及线程、锁以及原子操作等概念。 1. **线程**: - **创建...

    JAVA多线程与并发学习总结.pdf

    本文总结了JAVA多线程与并发的相关知识点,涵盖计算机系统的高速缓存机制、JAVA内存模型、内存间交互操作、volatile型变量、原子性、可见性与有序性、先行发生原则等内容。 计算机系统使用高速缓存来作为内存与...

    java多线程的讲解和实战

    Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要。本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者...

    Java多线程并发访问解决方案

    在Java编程中,多线程并发...以上是Java多线程并发访问的主要解决方案,理解并熟练运用这些技术,可以有效地解决并发编程中遇到的问题,提升程序的稳定性和效率。在实际开发中,应根据具体需求选择合适的并发控制策略。

    Java 多线程与并发编程总结.doc

    Java多线程与并发编程是Java开发中不可或缺的一部分,它涉及到如何高效地利用CPU资源,实现并发执行多个任务。在操作系统层面,多线程是为了提高系统利用率,使得多个任务能够"同时"执行,但实际上,由于CPU的时钟...

    张孝祥Java多线程与并发库高级应用视频教程练习代码

    本教程的焦点在于“张孝祥Java多线程与并发库高级应用视频教程”的实践代码,旨在帮助开发者深入理解并熟练掌握这些关键概念。 首先,我们要明确多线程的概念。在单处理器系统中,多线程允许程序同时执行多个任务,...

    java多线程Demo

    Java多线程是Java编程中的一个重要概念,它允许程序同时执行多个任务,提高了程序的效率和响应速度。在Java中,实现多线程有两种主要方式:继承Thread类和实现Runnable接口。 1. 继承Thread类: 当我们创建一个新...

    Java 多线程与并发(1-26)-Java 并发 - 理论基础.pdf

    Java 中的多线程机制的主要目标是解决并发问题,包括可见性、原子性和有序性三个方面。可见性问题是指一个线程对共享变量的修改,另一个线程不能立即看到。原子性问题是指一个操作或者多个操作要么全部执行并且执行...

    java多线程并发编程 核心技术应用实践

    Java多线程并发编程是Java开发中的重要领域,它涉及到如何高效地利用计算机资源,特别是在多核处理器系统中,能够显著提升程序的执行效率。在《java多线程并发编程核心技术应用实践》中,我们将深入探讨Java平台上的...

    java多线程模拟处理银行的实时转账交易

    本项目"java多线程模拟处理银行的实时转账交易"旨在通过多线程技术来模拟实现这一过程,确保程序的可扩展性和高效率。 首先,我们需要理解多线程的基本概念。线程是操作系统分配CPU时间片的基本单位,一个进程中...

    超实用的Java并发多线程教程

    Java并发多线程是Java编程中的重要组成部分,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,多线程主要通过`Thread`类、`Runnable`接口以及`ExecutorService`来实现。下面我们将深入探讨...

    JAVA多线程的锁机制和无锁并行.docx

    JAVA 多线程的锁机制和无锁并行 JAVA 多线程编程中,锁机制是保证线程安全的重要手段之一。锁机制可以分为内部锁和外部锁两种,内部锁又称为监视器或内部锁,它是一种非公平的排它锁,能够保障原子性、可见性和...

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...

    人工智能-项目实践-多线程-Java多线程高并发实例.zip

    通过分析和运行这些代码,你将能够更深入地理解Java多线程在高并发场景下的实际运用,从而在你的人工智能项目中实现更高效、更稳定的数据处理。 总之,这个项目实例旨在帮助开发者掌握Java多线程技术,提升处理高...

    Java多线程练习题

    Java多线程是Java编程中的核心概念,它允许程序同时执行多个任务,提高了系统的效率和响应性。在Java中,多线程的实现主要通过两种方式:继承Thread类和实现Runnable接口。理解并掌握多线程的使用对于任何Java开发者...

    Java多线程实战精讲-带你一次搞明白Java多线程高并发

    Java多线程实战精讲是Java...以上内容覆盖了Java多线程的核心知识点,通过学习和实践,你将能够熟练应对各种高并发场景,提升代码的效率和稳定性。在Java世界里,理解和掌握多线程是成为一个高级开发者的必经之路。

Global site tag (gtag.js) - Google Analytics