多线程并发之原子性(六)

最近在网上找到好多的多线程关于原子性的例子,说的都不是非常的明确,对于刚学习多线程的新手而言很容误导学员,在这里,我通过多个例子对多线程的原子性加以说明。

例子一:传统技术自增

package face.thread.volatilep;

public class Counter2 {
    private  int count = 0;
    public synchronized void inc() {
        count = count + 1;
    }

    public static void main(String[] args) {

        //同时启动1000个线程,去进行i++计算,看看实际结果
    	final Counter2 c = new Counter2();

        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                	c.inc();
                }
            }).start();
        }

        try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
        //这里每次运行的值都有可能不同,可能为1000
        System.out.println("运行结果:Counter.count=" + c.count);
    }
}

以上代码打印的结果偶尔会等于1000,基本上都会有一些误差,原因是线程执行的顺序无法保证的,很可能在新建的1000个线程还没有执行完,我们的代码

  System.out.println("运行结果:Counter.count=" + Counter.count);

就已经执行完了,要想解决这个问题很简单,那就是在最后一句println之前在线程睡眠一段时间,比如睡眠2秒钟。等1000个线程执行完了,在打印"

"运行结果:Counter.count=" + Counter.count";

还可以借助于线程辅助类解决,在这里就举例一个最简单的例子展示:

package face.thread.volatilep;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class Counter3 {

     int count =0;
    public synchronized  void inc() {
    	count++;
    }

    public static void main(String[] args) {

        //同时启动1000个线程,去进行i++计算,看看实际结果
    	final Counter3 c = new Counter3();

       final CyclicBarrier cy = new CyclicBarrier(10000, new Runnable() {
  			public void run() {
  		        //这里每次运行的值都有可能不同,可能为1000
  		        System.out.println("运行结果:Counter.count=" + c.count);
  			}
  		});

        for (int i = 0; i < 10000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                	c.inc();
                	try {
						cy.await();
					} catch (InterruptedException e) {
						e.printStackTrace();
					} catch (BrokenBarrierException e) {
						e.printStackTrace();
					}
                }
            }).start();
        }

    }
}

例子二:原子性自增

原子是世界上的最小单位,具有不可分割性。比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++; 这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。Java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。

因为原子性是线程安全的,所以关于原子性自增是不需要传统的加锁技术的,具体看代码:

package face.thread.volatilep;

import java.util.concurrent.atomic.AtomicInteger;

public class CounterNew2{

	AtomicInteger count = new AtomicInteger(0);

	public void increment() {
		count.getAndIncrement();
	}

	public int getCount() {
		return count.get();
	}

	public static void main(String[] args) {

		final CounterNew2 cn = new CounterNew2();
		for(int i = 0 ; i < 10000;i++){
			new Thread(new Runnable() {
				public void run() {
					cn.increment();
				}
			}).start();
		}

		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("count最终返回值:" + cn.getCount());
	}
}

运行结果也是:10000

时间: 2024-08-19 16:58:34

多线程并发之原子性(六)的相关文章

iOS多线程开发之GCD(下篇)

上篇和中篇讲解了什么是GCD,如何使用GCD,这篇文章将讲解使用GCD中将遇到的死锁问题.有兴趣的朋友可以回顾<iOS多线程开发之GCD(上篇)>和<iOS多线程开发之GCD(中篇)>. 言归正传,我们首先来回顾下死锁,所谓死锁: 是指两个或两个以上的进程(线程)在执行过程中,因争夺资源(如数据源,内存等,变量不是资源)而造成的一种互相等待的现象,若无外部处理作用,它们都将无限等待下去. 死锁形成的原因: 系统资源不足 进程(线程)推进的顺序不恰当: 资源分配不当 死锁形成的条件:

iOS多线程开发之NSOperation - 快上车,没时间解释了!

一.什么是NSOperation? NSOperation是苹果提供的一套多线程解决方案.实际上NSOperation是基于GCD更高一层的封装,但是比GCD更加的面向对象.代码可读性更高.可控性更强,很屌的是加入了操作依赖. 默认情况下,NSOperation单独使用时只能同步执行操作,并没有开辟新线程的能力,只有配合NSOperationQueue才能实现异步执行.讲到这里,我们不难发现GCD和NSOperation实现的方式很像,其实这更像是废话,NSOperation本身就是基于GCD的

“全栈2019”Java多线程第四十六章:判断任意线程是否已持有写锁

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多线程第四十六章:判断任意线程是否已持有写锁 下一章 "全栈2019"Java多线程第四十七章:判断锁是否为公平锁isFair() 学习小组 加入同步学习小组,共同交流与进步. 方式一:加入编程圈子. 方式二:关注头条号Gorhaf,私信"Java学习小组". 方式三:关

多线程并发之java内存模型JMM

多线程概念的引入是人类又一次有效压寨计算机的体现,而且这也是非常有必要的,因为一般运算过程中涉及到数据的读取,例如从磁盘.其他系统.数据库等,CPU的运算速度与数据读取速度有一个严重的不平衡,期间如果按一条线程执行将会在很多节点产生阻塞,使计算效率低下.另外,服务器端是java最擅长的领域,作为服务器必须要能同时响应多个客户端的请求,同样需要多线程的支持.在多线程情况下,高并发将带来数据的共享与竞争问题,tomcat作为中间件将多线程并发等细节尽量封装起来处理,使用户对多线程透明,更多地关注业务

并发之原子性、可见性、有序性

volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以重获生机. volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情.由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用vola

Java 多线程与并发(六):AQS

我们前面几张提到过,JUC 这个包里面的工具类的底层就是使用 CAS 和 volatile 来保证线程安全的,整个 JUC 包里面的类都是基于它们构建的.今天我们介绍一个非常重要的同步器,这个类是 JDK 在 CAS 和 volatile 的基础上为我们提供的一个同步工具类. 背景 AbstractQueuedSynchronizer,JDK 1.5 引入了 JUC 包,这个包提供了一些列支持并发的组件,这些组件是一些列同步器,他们主要完成以下功能: 内部状态的管理和更新,比如表示一个锁的状态是

iOS多线程开发之GCD 用法入门

我们知道,在iOS中进行多线程编程,主要有三种方式:[NSThread].[NSOperation]和[GCD].其中又以[GCD]为苹果官方最为推荐.本文将利用一个简单的demo,简述GCD的用法入门,以及本人对GCD的一点肤浅理解和学习心得. 先把参考文章列出: http://www.cnblogs.com/kenshincui/p/3983982.html http://www.cnblogs.com/sunfrog/p/3305614.html http://mobile.51cto.c

C++多线程开发之actor model

最近想把写过的一个多线程程序整理一下,这个程序主要特点是有一系列的互相之间有依赖关系的task.于是在网上找相关类库 1,一类是简单的线程池了,这也是原本俺的做法.之前使用的是手工调度,代码实现的很蛋疼.外面的lib有poco https://pocoproject.org/slides/130-Threads.pdf2,Intel TBB, MS PPL (Parallel Patterns Library)之类的类库,感觉这里一类本质上和1没有大的分别3,微软的并行库1)MS PPL (Pa

IOS多线程开发之NSThread

概要 使用NSThread的例子,线程创建.启动.线程同步.锁.线程的交互,需要注意的时线程的交互,因为IOS规定只有主线程能够修改UI,所以如果子线程要修改UI的话,需要与主线程交互,即调用方法- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;实现 示例代码 // // ViewController.m // NSThreadDemo // // Crea