how2j.cn

本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频



5分43秒
本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器。 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)。 chrome 的 视频下载插件会影响播放,如 IDM 等,请关闭或者切换其他浏览器



步骤 1 : 原子性操作概念   
步骤 2 : AtomicInteger   
步骤 3 : 同步测试   
步骤 4 : 练习-使用AtomicInteger来替换Hero类中的synchronized   
步骤 5 : 答案-使用AtomicInteger来替换Hero类中的synchronized   

步骤 1 :

原子性操作概念

edit
所谓的原子性操作即不可中断的操作,比如赋值操作

int i = 5;

原子性操作本身是线程安全的
但是 i++ 这个行为,事实上是有3个原子性操作组成的。
步骤 1. 取 i 的值
步骤 2. i + 1
步骤 3. 把新的值赋予i
这三个步骤,每一步都是一个原子操作,但是合在一起,就不是原子操作。就不是线程安全的。
换句话说,一个线程在步骤1 取i 的值结束后,还没有来得及进行步骤2,另一个线程也可以取 i的值了。
这也是分析同步问题产生的原因 中的原理。
i++ ,i--, i = i+1 这些都是非原子性操作。
只有int i = 1,这个赋值操作是原子性的。
JDK6 以后,新增加了一个包java.util.concurrent.atomic,里面有各种原子类,比如AtomicInteger
而AtomicInteger提供了各种自增,自减等方法,这些方法都是原子性的。 换句话说,自增方法 incrementAndGet 是线程安全的,同一个时间,只有一个线程可以调用这个方法。
package multiplethread; import java.util.concurrent.atomic.AtomicInteger; public class TestThread { public static void main(String[] args) throws InterruptedException { AtomicInteger atomicI =new AtomicInteger(); int i = atomicI.decrementAndGet(); int j = atomicI.incrementAndGet(); int k = atomicI.addAndGet(3); } }
package multiplethread;
  
import java.util.concurrent.atomic.AtomicInteger;
  
public class TestThread {
  
    public static void main(String[] args) throws InterruptedException {
    	AtomicInteger atomicI =new AtomicInteger();
    	int i = atomicI.decrementAndGet();
    	int j = atomicI.incrementAndGet();
    	int k = atomicI.addAndGet(3);
    	
    }
  
}
分别使用基本变量的非原子性的++运算符和 原子性的AtomicInteger对象的 incrementAndGet 来进行多线程测试。
测试结果如图所示
同步测试
package multiplethread; import java.util.concurrent.atomic.AtomicInteger; public class TestThread { private static int value = 0; private static AtomicInteger atomicValue =new AtomicInteger(); public static void main(String[] args) { int number = 100000; Thread[] ts1 = new Thread[number]; for (int i = 0; i < number; i++) { Thread t =new Thread(){ public void run(){ value++; } }; t.start(); ts1[i] = t; } //等待这些线程全部结束 for (Thread t : ts1) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.printf("%d个线程进行value++后,value的值变成:%d%n", number,value); Thread[] ts2 = new Thread[number]; for (int i = 0; i < number; i++) { Thread t =new Thread(){ public void run(){ atomicValue.incrementAndGet(); } }; t.start(); ts2[i] = t; } //等待这些线程全部结束 for (Thread t : ts2) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.printf("%d个线程进行atomicValue.incrementAndGet();后,atomicValue的值变成:%d%n", number,atomicValue.intValue()); } }
步骤 4 :

练习-使用AtomicInteger来替换Hero类中的synchronized

edit  姿势不对,事倍功半! 点击查看做练习的正确姿势
给Hero的方法加上修饰符synchronized 这个知识点中,通过给hurt和 recover方法加上synchronized来达到线程安全的效果。

这一次换成使用AtomicInteger来解决这个问题

提示:int基本类型对应的是AtomicInteger,但是float基本类型没有对应的AtomicFloat。 所以在这个练习中,把hp改为AtomicInteger即可。
步骤 5 :

答案-使用AtomicInteger来替换Hero类中的synchronized

edit
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
账号未激活 账号未激活,功能受限。 请点击激活
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频

4分38秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器。 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)。 chrome 的 视频下载插件会影响播放,如 IDM 等,请关闭或者切换其他浏览器


给Hero的方法加上修饰符synchronized 这个知识点中,通过给hurt和 recover方法加上synchronized来达到线程安全的效果。

这一次换成使用AtomicInteger来解决这个问题
把Hero的hp设计为

AtomicInteger hp = new AtomicInteger();

recover和hurt上的synchronized修饰符去掉
recover中调用

hp.incrementAndGet();

hurt中调用

hp.decrementAndGet();
答案-使用AtomicInteger来替换Hero类中的synchronized
package charactor; import java.util.concurrent.atomic.AtomicInteger; public class Hero{ public String name; public AtomicInteger hp = new AtomicInteger(); public int damage; public void recover(){ hp.incrementAndGet(); } public void hurt(){ hp.decrementAndGet(); } public void attackHero(Hero h) { h.hp.addAndGet(0-damage); System.out.format("%s 正在攻击 %s, %s的血变成了 %.0f%n",name,h.name,h.name,h.hp); if(h.isDead()) System.out.println(h.name +"死了!"); } public boolean isDead() { return 0>=hp.intValue()?true:false; } }
package multiplethread; import java.lang.reflect.GenericSignatureFormatError; import charactor.Hero; public class TestThread { public static void main(String[] args) { final Hero gareen = new Hero(); gareen.name = "盖伦"; gareen.hp.set(10000); int n = 10000; Thread[] addThreads = new Thread[n]; Thread[] reduceThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread(){ public void run(){ //recover自带synchronized gareen.recover(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); addThreads[i] = t; } for (int i = 0; i < n; i++) { Thread t = new Thread(){ public void run(){ //hurt自带synchronized gareen.hurt(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); reduceThreads[i] = t; } for (Thread t : addThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (Thread t : reduceThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.printf("%d个增加线程和%d个减少线程结束后%n盖伦的血量是 %.0f%n", n,n,gareen.hp.floatValue()); } }


HOW2J公众号,关注后实时获知最新的教程和优惠活动,谢谢。


问答区域    
2024-08-14 练习-使用AtomicInteger来替换Hero类中的synchronized
木宇




练习-使用AtomicInteger来替换Hero类中的synchronized
package com.cwt.study.java中级.s_20240813;

import java.util.concurrent.atomic.AtomicInteger;

public class Hero {
    public AtomicInteger hp = new AtomicInteger();
    public String name;
    public int damage;

    //回血
    //直接在方法前加上修饰符synchronized
    //其所对应的同步对象,就是this
    //和hurt方法达到的效果一样
    public  void recover(){
        hp.incrementAndGet();
    }

    //掉血
    public void hurt(){
        //使用this作为同步对象
        this.hp.decrementAndGet();

    }

    public void attackHero(Hero h) {
        h.hp.addAndGet(0-damage);
        System.out.format("%s 正在攻击 %s, %s的血变成了 %.0f%n",name,h.name,h.name,h.hp);
        if(h.isDead())
            System.out.println(h.name +"死了!");
    }

    public boolean isDead() {
        return 0>=hp.intValue()?true:false;
    }

}


package com.cwt.study.java中级.s_20240813;

public class TestThread {
    public static void main(String[] args) {

        final Hero gareen = new Hero();
        gareen.name = "盖伦";
        gareen.hp.set(10000);
        int n = 10000;

        Thread[] addThreads = new Thread[n];
        Thread[] reduceThreads = new Thread[n];

        for (int i = 0; i < n; i++) {
            Thread t = new Thread(){
                public void run(){

                    //recover自带synchronized
                    gareen.recover();

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            };
            t.start();
            addThreads[i] = t;

        }

        for (int i = 0; i < n; i++) {
            Thread t = new Thread(){
                public void run(){
                    //hurt自带synchronized
                    gareen.hurt();

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            };
            t.start();
            reduceThreads[i] = t;
        }

        for (Thread t : addThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        for (Thread t : reduceThreads) {
            try {
                t.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        System.out.printf("%d个增加线程和%d个减少线程结束后%n盖伦的血量是 %.0f%n", n,n,gareen.hp.floatValue());

    }
}

							





回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2024-07-14 Automic方法在多线程下实现线程安全
虚心求学




分别对比,线程安全类和非线程安全类的实验结果: ————————线程不安全类测试————————: 初始值: [ GAREEN,1000 ] 进行50000次增减操作后: [ GAREEN,999 ] ————————线程安全类测试————————: 初始值: [ GAREEN,1000 ] 进行50000次增减操作后: [ GAREEN,1000 ]
package testThread;

import java.util.concurrent.atomic.AtomicInteger;

public class TestAtomicNumber {
	public static void main(String[] args) {
		int testNum = 5*10000;
		System.out.println("\r\n————————线程不安全类测试————————:\r\n");
		testThread(new Hero("gareen", 1000),testNum);
		System.out.println("\r\n————————线程安全类测试————————:\r\n");
		testThread(new AtomicHero("gareen", 1000),testNum);
	}
	public static void testThread(CharacterHero gareen,int testCount)
	{
		final int count = testCount;
		System.out.println("初始值:");
		System.out.println(gareen);
		System.out.printf("%n进行%d次增减操作后:%n", count);
		Thread[]ts = new Thread[2*count];
		for (int i = 0; i < count; i++) {
			Thread t1 = new Thread(() -> {
				gareen.hurt();
			});
			t1.start();
			ts[i] = t1;
		}
		for (int i = 0; i < count; i++) {
			Thread t1 = new Thread(() -> {
				gareen.recover();
			});
			t1.start();
			ts[i+count] = t1;
		}
		for (Thread t : ts) {
			try {
				t.join();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(gareen);
	}
}
abstract class CharacterHero{
	public abstract void hurt();
	public abstract void recover();
}
class AtomicHero extends CharacterHero{
	public AtomicInteger atomHP;
	public String name;

	public AtomicHero(String name, int hp) {
		this.name = name;
		this.atomHP = new AtomicInteger(hp);
	}

	public void hurt() {
		atomHP.decrementAndGet();
	}

	public void recover() {
		atomHP.incrementAndGet();
	}

	@Override
	public String toString() {
		return String.format("[ %S,%d ]", name, atomHP.intValue());
	}
}
class Hero extends CharacterHero{
	public int hp;
	public String name;

	public Hero(String name, int hp) {
		this.name = name;
		this.hp = hp;
	}

	public void hurt() {
		hp--;
	}

	public void recover() {
		hp++;
	}

	@Override
	public String toString() {
		return String.format("[ %S,%d ]", name, hp);
	}
}

							





回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2022-10-03 因为number为100000,数量大,就试了一下用线程池
2021-08-18 练习 - 使用AtomicInteger来替换Hero类中的synchronized
2021-05-06 .....


提问太多,页面渲染太慢,为了加快渲染速度,本页最多只显示几条提问。还有 5 条以前的提问,请 点击查看

提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
关于 JAVA 中级-多线程-原子访问 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 578362961
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: https://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图