首页 > 编程语言 > Java编程思想22

Java编程思想22

时间:2022-12-26 22:35:40浏览次数:42  
标签:Java 22 java void 编程 Pair new public pm

3. 关于volatile
  volatile关键字确保了应用中的可视性。如果你将一个域声明为volatile的,那么只要对这个域产生了写操作,那么所有的读操作就都可以看到这个修改。即便使用了本地缓存,情况也确实如此,volatile域会立即被写入到主存中,而读取操作就发生在主存中。
  理解原子性和易变性是不同的概念这一点很重要。在非volatile域上的原子操作不必刷新到主存中去,因此其他读取该域的任务也不必看到这个新值。如果多个任务在同时访问某个域,那么这个域就应该是volatile的,否则,这个域就应该只能经由同步来访问。同步也会导致向主存中刷新,因此如果一个域完全由synchronized方法或语句块来防护,那就不必将其设置为是volatile的。

  使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域。再次提醒,你的第一选择应该是使用synchronized关键字,这是最安全的方式,而尝试其他任何方式都是有风险的。

原子类
  Java SE5引入了诸如AtomicInteger、AtomicLong、AtomicReference等特殊的原子性变量类,它们提供下面形式的原子性条件更新操作∶

boolean compareAndSet(expectedValue, updateValue);

这些类被调整为可以使用在某些现代处理器上的可获得的,并且是在机器级别上的原子性,因此在使用它们时,通常不需要担心。对于常规编程来说,它们很少会派上用场,但是在涉及性能调优时,它们就大有用武之地了。例如,我们可以使用AtomicInteger来重写MutexEventGenerator.java∶

package concurrency;

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

/**
* @author Mr.Sun
* @date 2022年09月04日 15:17
*
* 使用AtomicInteger来重写MutexEventGenerator
*/
public class AtomicEventGenerator extends IntGenerator {
private AtomicInteger currentEventVal = new AtomicInteger(0);

@Override
public int next() {
return currentEventVal.addAndGet(2);
}

public static void main(String[] args) {
EventChecker.test(new AtomicEventGenerator());
}
}

 

所有其他形式的同步通过使用AtomicInteger得到了根除。
  应该强调的是,Atomic类被设计用来构建javautil.concurrent中的类,因此只有在特殊情况下才在自己的代码中使用它们,即便使用了也需要确保不存在其他可能出现的问题。通常依赖于锁要更安全一些(要么是synchronized关键字,要么是显式的Lock对象)。

临界区
  有时,你只是希望防止多个线程同时访问方法内部的部分代码而不是防止访问整个方法。通过这种方式分离出来的代码段被称为临界区(critical section),它也使用synchronized关键字建立。这里,synchronized被用来指定某个对象,此对象的锁被用来对花括号内的代码进行同步控制∶

synchronized(syncObject) {
// This code can be accessed
// by only one task at a time 1
}

 这也被称为同步拉制块;在进入此段代码前,必须得到syncObject对象的锁。如果其他线程已经得到这个锁,那么就得等到锁被释放以后,才能进入临界区。

  通过使用同步控制块,而不是对整个方法进行同步控制,可以使多个任务访问对象的时间性能得到显著提高,下面的例子比较了这两种同步控制方法。此外,它也演示了如何把一个非保护类型的类,在其他类的保护和控制之下,应用于多线程的环境∶

package concurrency;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* @author Mr.Sun
* @date 2022年09月04日 15:27
*
* 临界区 测试
* <p>
* 演示了如何把一个非线程安全的类,在其他类的保护和控制之下,应用于多线程的环境
* </p>
*/
public class CriticalSelection {
public static void main(String[] args) {
PairManager pman1 = new PairManager1(),
pman2 = new PairManager2();

testApproaches(pman1, pman2);
}

// 测试两种不同的方法
static void testApproaches(PairManager pman1, PairManager pman2) {
ExecutorService exec = Executors.newCachedThreadPool();
PairManipulator
pm1 = new PairManipulator(pman1),
pm2 = new PairManipulator(pman2);

PairChecker
pcheck1 = new PairChecker(pman1),
pcheck2 = new PairChecker(pman2);

exec.execute(pm1);
exec.execute(pm2);

exec.execute(pcheck1);
exec.execute(pcheck2);

try {
TimeUnit.MILLISECONDS.sleep(200);
} catch(InterruptedException e) {
System.out.println("Sleep interrupted");
} finally {
exec.shutdown();
}
System.out.println("pm1: " + pm1 + "\npm2: " + pm2);
System.exit(0);
}
}

class PairChecker implements Runnable {
private PairManager pm;
public PairChecker(PairManager pm) {
this.pm = pm;
}

@Override
public void run() {
while(true) {
pm.checkCounter.incrementAndGet();
pm.getPair().checkState();
}
}
}

class PairManipulator implements Runnable {
private PairManager pm;

public PairManipulator(PairManager pm) {
this.pm = pm;
}

@Override
public void run() {
while(true) {
pm.increment();
}
}

public String toString() {
return "Pair: " + pm.getPair() + " checkCounter = " + pm.checkCounter.get();
}
}

// 线程安全的Pair
abstract class PairManager {
AtomicInteger checkCounter = new AtomicInteger(0);
protected Pair p = new Pair();
private List<Pair> storage = Collections.synchronizedList(new ArrayList<>());

public synchronized Pair getPair() {
// 复制一份以确保原件的安全:
return new Pair(p.getX(), p.getY());
}

// 假设这是一个耗时的操作
protected void store(Pair p) {
storage.add(p);
try {
TimeUnit.MILLISECONDS.sleep(50);
} catch (InterruptedException ignore) {}
}

public abstract void increment();
}

// 同步整个方法
class PairManager1 extends PairManager {

@Override
public synchronized void increment() {
p.incrementX();
p.incrementY();
store(getPair());
}
}

// 同步临界区代码块
class PairManager2 extends PairManager {

@Override
public void increment() {
Pair temp;
synchronized(this) {
p.incrementX();
p.incrementY();
temp = getPair();
}
store(temp);
}
}

// 非线程安全
class Pair {
private int x, y;

public Pair(int x, int y) {
this.x = x;
this.y = y;
}

public Pair() {
this(0, 0);
}

public int getX() {
return x;
}

public int getY() {
return y;
}

public void incrementX () {
// 自增操作不是线程安全的
x++;
}

public void incrementY () {
// 自增操作不是线程安全的
y++;
}

@Override
public String toString() {
return "x: " + x + ", y: " + y ;
}

public class PairValueNotEqualException extends RuntimeException {
public PairValueNotEqualException() {
super("一对不相等的值:" + Pair.this);
}
}

// 任意不变量 —— 两个变量必须相等
public void checkState() {
if(x != y) {
throw new PairValueNotEqualException();
}
}
} /* Output: (Sample)
pm1: Pair: x: 15, y: 15 checkCounter = 272565
pm2: Pair: x: 16, y: 16 checkCounter = 3956974
*///:~

 正如注释中注明的,Pair不是线程安全的,因为它的约束条件(虽然是任意的)需要两个变量要维护成相同的值。此外,如本章前面所述,自增加操作不是线程安全的,并且因为Pair类没有任何方法被标记为synchronized,所以不能保证一个Pair对象在多线程程序中不会被破坏。

标签:Java,22,java,void,编程,Pair,new,public,pm
From: https://www.cnblogs.com/LvJinshuai/p/17007065.html

相关文章

  • 套接字编程
                           ......
  • 网络高级编程
    非阻塞和异步I/O在socket编程中可以使用函数fcntl(intfd,intcmd,intarg)的如下的编程特性。获得文件状态标志:将cmd设置为F_GETFL,会返回由fd指向的文件的状态标志。非......
  • 2022 International Collegiate Programming Contest, Jinan Site 部分题目简要题解
    从这里开始比赛目录傻逼学院负责人ProblemBTorch注意到$a_1,b_1,a_2,b_2$的和不会超过$10^6$考虑胖先生的周期开始的时候,瘦先生的周期在时......
  • 正确理解和使用JAVA中的字符串常量池
    前言研究表明,Java堆中对象占据最大比重的就是字符串对象,所以弄清楚字符串知识很重要,本文主要重点聊聊字符串常量池。Java中的字符串常量池是Java堆中的一块特殊存储区域,用......
  • 【JAVA笔记】JAVA的StringBuilder和StringBuffer类、Data类和Calendar类、基本类型的
    一、StringBuilder和StringBuffer类 实例:packagecn.test02.demo6;publicclassTest1{publicstaticvoidmain(String[]args){//测试构造方法......
  • 2022年终总结
    今年主要成就是:(1)身体调整到最佳状态(2)工作有些变动但最终换成了自己满意的工作(我的优先级:研究院>券商>web3>传统工业>web2)(3)入门玄学并认识了很多朋友(4)工作站各项配件的配置......
  • Java——I/O流
    文章目录​​一.I/O流概述​​​​二.I/O流的分类​​​​三.流应该怎么学习​​​​四.IO流四大家族​​​​五.需要掌握的流​​​​1.java.io.FileInputStream常用......
  • Java——多线程
    文章目录​​一.多线程概述​​​​1.什么是进程?什么是线程?​​​​2.进程和线程的关系​​​​3.多线程并发​​​​4.分析以下程序有几个线程​​​​5.Java实现线......
  • java中Integer的细节【面试】
    本文主要讲述Integer包装类的细节,通过创建Integer类的方式不同,探讨区别示例代码如下:1publicclassIntegerExercise{2publicstaticvoidmain(String[]args......
  • DASCTFxGFCTF2022 RSA
    又水一篇wpXDAnalyzefromCrypto.Util.numberimport*fromsecretimportflagdefencrypt1(n):n1=hex(n>>200).encode()n2=str(hex(n))[20:].encode......