对于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语言中用于处理多任务执行的关键技术,它能够帮助开发者设计出能够有效应对高并发请求的应用程序。在现代的线上(Online)和离线(Offline)应用中,合理利用多线程技术可以大幅提高系统...
原子操作是 Java 多线程并发编程中非常重要的一方面。面试官通常会问一些相关的问题,以检测候选者的基础知识和实际编程能力。 * 什么是原子操作,Java 中的原子操作是什么? 这个问题是检测候选者对原子操作的理解...
Java多线程与锁是Java并发编程中的核心概念,它们对于构建高效、可扩展的并发应用程序至关重要。在Java中,多线程允许程序同时执行多个任务,提高CPU的利用率,而锁则是用来控制多线程间共享资源的访问,确保数据的...
在Java编程中,多线程是处理并发执行的关键技术,它允许程序同时执行多个任务,提高了系统的效率和响应性。本资料主要探讨了Java中的并发模式和特性,涉及线程、锁以及原子操作等概念。 1. **线程**: - **创建...
根据给定文件的信息,我们可以提炼出以下关于Java多线程与并发库的相关知识点: ### Java多线程基础 1. **线程的概念**:在Java中,线程是程序执行流的基本单元。一个标准的Java应用程序至少有一个线程,即主...
Java多线程是Java编程语言中一个非常重要的概念,它允许开发者在一个程序中创建多个执行线程并行运行,以提高程序的执行效率和响应速度。在Java中,线程的生命周期包含五个基本状态,分别是新建状态(New)、就绪...
Java多线程是Java编程中的重要概念,尤其在如今的多核处理器环境下,理解并熟练掌握多线程技术对于提高程序性能和响应速度至关重要。本资料详细讲解了Java多线程的原理,并提供了丰富的实战代码,非常适合Java初学者...
在Java编程中,多线程并发...以上是Java多线程并发访问的主要解决方案,理解并熟练运用这些技术,可以有效地解决并发编程中遇到的问题,提升程序的稳定性和效率。在实际开发中,应根据具体需求选择合适的并发控制策略。
Java多线程与并发编程是Java开发中不可或缺的一部分,它涉及到如何高效地利用CPU资源,实现并发执行多个任务。在操作系统层面,多线程是为了提高系统利用率,使得多个任务能够"同时"执行,但实际上,由于CPU的时钟...
本教程的焦点在于“张孝祥Java多线程与并发库高级应用视频教程”的实践代码,旨在帮助开发者深入理解并熟练掌握这些关键概念。 首先,我们要明确多线程的概念。在单处理器系统中,多线程允许程序同时执行多个任务,...
本项目"java多线程模拟处理银行的实时转账交易"旨在通过多线程技术来模拟实现这一过程,确保程序的可扩展性和高效率。 首先,我们需要理解多线程的基本概念。线程是操作系统分配CPU时间片的基本单位,一个进程中...
Java多线程和并发知识是Java开发中的重要组成部分,它涉及到如何高效地利用系统资源,尤其是在多核CPU环境下,合理地使用多线程可以显著提升应用程序的性能。 **1. 理论基础** 1.1 为什么需要多线程 多线程的引入...
Java多线程是Java编程中的一个重要概念,它允许程序同时执行多个任务,提高了程序的效率和响应速度。在Java中,实现多线程有两种主要方式:继承Thread类和实现Runnable接口。 1. 继承Thread类: 当我们创建一个新...
以上只是Java多线程、锁和内存模型的一部分核心概念,深入理解这些知识点对于编写高效、可靠的并发程序至关重要。在面试中,面试官通常会通过这些问题来评估候选人的并发编程能力。因此,对这些概念的深入理解和实践...
在这个例子中,我们看到一个简单的Java多线程程序,用于模拟售票系统,其中有两个类`SellThread`和`AddThread`分别代表售票和加票的操作。 首先,我们来理解一下`SellThread`类。这个类实现了`Runnable`接口,这...
Java 中的多线程机制的主要目标是解决并发问题,包括可见性、原子性和有序性三个方面。可见性问题是指一个线程对共享变量的修改,另一个线程不能立即看到。原子性问题是指一个操作或者多个操作要么全部执行并且执行...