带有 AspectJ 切入点的 ReentrantReadWriteLock 用于每个初始化类型的 MyStructure [英] ReentrantReadWriteLock with AspectJ pointcut for every initialized type of MyStructure

查看:24
本文介绍了带有 AspectJ 切入点的 ReentrantReadWriteLock 用于每个初始化类型的 MyStructure的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力使用 AspectJ 为每个构建的对象创建一个 ReentrantReadWriteLock 并且是一种 Mystructure.这是我的源代码.

I am struggling to create a ReentrantReadWriteLock with AspectJ for every single object that is constructed and is a type of Mystructure. Here is my source code.

切面类

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Aspect
public class LocksAspect {
    private ReentrantReadWriteLock rwLock;
    private Lock acquireReadLock;
    private Lock acquireWriteLock;

    @Before("!within(LocksAspect)&&execution(*.new(..))")
    public void LookupBefores() {
        rwLock = new ReentrantReadWriteLock();
        acquireReadLock = rwLock.readLock();
        acquireWriteLock = rwLock.writeLock();
    }

    @Pointcut("call(void MyStructure.Insert(String))")
    public void InsertPointcut() {
    }

    @Pointcut("call(void MyStructure.Read(int))")
    public void ReadPointcut() {
    }

    @Before("InsertPointcut()")
    public void InsertPointcutBefore(JoinPoint pointcut) throws InterruptedException {
        acquireWriteLock.lock();
        String thrdName = Thread.currentThread().getName();
        System.out.println(thrdName + "  is entering in critical Section {} ");
        Thread.sleep(10000);
    }


    @After("InsertPointcut()")
    public void InsertPointcutAfter(JoinPoint pointcut) {
        String thrdName = Thread.currentThread().getName();
        System.out.println(thrdName + " received notification and is exiting critical Section {} ");
        acquireWriteLock.unlock();
    }

    @Before("ReadPointcut()")
    public void ReadPointcutBefore(JoinPoint pointcut) throws InterruptedException {
        acquireReadLock.lock();
        String thrdName = Thread.currentThread().getName();
        System.out.println(thrdName + "  is entering in critical Section {} ");
        Thread.sleep(1000);
    }


    @After("ReadPointcut()")
    public void ReadPointcutAfter(JoinPoint pointcut) {
        String thrdName = Thread.currentThread().getName();
        System.out.println(thrdName + " received notification and is exiting critical Section {} ");
        acquireReadLock.unlock();
    }
}

Thread writer 类.(Reader 线程类不重要,因为我的问题不同所以我省略了)

The Thread writer class.(The Reader thread class is not important because my problem is different so i omitted it)

public class Writer extends Thread{
   private MyStructure myStructure;
    public Writer(MyStructure myStructure) {
        this.myStructure=myStructure;
    }

    @Override
    public void run() {
        this.myStructure.Insert("example");
    }
}

我的结构类

import java.util.ArrayList;

public class MyStructure {
    ArrayList<String> examplelist;

    public MyStructure() {
        examplelist = new ArrayList<String>();
    }

    public void Insert(String value) {
        examplelist.add(value);
    }

    public void Read(int pos) {
        examplelist.get(pos);
    }
}

主要内容

MyStructure structure = new MyStructure();
        MyStructure structure1 = new MyStructure();
        new Thread(new Writer(structure), "Thread1").start();
        new Thread(new Writer(structure1), "Thread2").start();

输出

Thread2  is entering in critical Section {} 
Thread2 received notification and is exiting critical Section {} 
Thread1  is entering in critical Section {} //Thread1 will wait for Thread2 to release the lock in critical section   which is wrong
Thread1 received notification and is exiting critical Section {} 

现在我的问题是如何为创建的 Mystructure 的每个对象获得一个新的 ReentrantReadWriteLock.例如,如果我们运行上面的例子,Thread1 和 Thread2 都必须能够访问临界区,因为它们对对象有不同的引用,但这不应该发生.我的问题是 Thread2 将阻塞并等待 Thread1 完成这是错误的.如何使用 Aspect4j 绕过这个构建问题?

Now my problem is How I will get a new ReentrantReadWriteLock for each object of Mystructure that created. For example, if we run the above example both Thread1 and Thread2 must be able to access the critical section because they have different references of the object, but this should not have happened. My problem is that the Thread2 will block and wait for Thread1 to finish which is wrong. How can I bypass this problem of construction with Aspect4j?

推荐答案

解决问题的关键是每个 MyStructure 实例需要一组锁.不过,您的方面是单身人士.因此,您要么需要使用另一个方面实例化方案(这是我将在我的答案中使用的方案),要么通过保留一组锁并在 MyStructure 对象已创建.

The key to your problem's solution is that you need one set of locks per MyStructure instance. Your aspect is a singleton, though. So either you need to use another aspect instantiation scheme (which is what I will use in my answer) or do manual bookkeeping within the singleton aspect by keeping a set of locks and add a new element into that set whenever a MyStructure object is created.

为了更好地理解我的回答,关于方面实例化.

In order to better understand my answer, please refer to the AspectJ manual for information about aspect instantiation.

在我们开始之前,关于您的代码的一些评论以及我对其进行了一些更改的原因:

Before we start, a few comments concerning your code and why I have changed it a bit:

  • 你的Writer已经是一个Thread的子类,不需要再包装到另一个线程实例中.(我知道您可能只是为了能够命名线程而这样做,但这可以通过在您的类中添加一个构造函数来实现,该构造函数采用 name 参数并将其传递给超类构造函数.)
  • 您不应该调用 JoinPoint pointcut 类型的变量,因为连接点不是 AOP 方面的切入点.
  • 我将日志记录分解为自己的辅助方法并对其进行了一些改进,以便我们可以更清楚地看到什么时候会发生什么.
  • 我决定用一个 around 建议替换每对 before 和 after 建议.当然,这是可选的,但在这种情况下,我更喜欢在一个地方看到控制流.顺便说一句,如果您想针对非空方法,请小心将 around 建议的返回类型更改为 Object 并实际返回一些内容.这里没有必要,因为在这两种情况下我们都有 void 方法.
  • 我还决定内联切入点,这也是可选的,但为了演示目的,这里的示例代码更简洁一些.
  • 我添加了一个 Reader 类并使用它来展示可重入读锁与写锁之间的区别.
  • 我还负责使 MyStructure 实例可命名和可打印,以便在日志中更轻松地识别目标对象.
  • 我随机化了读取器/写入器线程的执行顺序,以便以更真实的方式混合它们.为了避免在写入之前从新创建的 MyStructure 读取时出现异常污染日志,我确保 MyStructure 在构造函数中获得一个默认元素.我不想在这里捕获异常以保持示例代码简单.
  • 我将方面放在另一个包中而不是应用程序代码中,以便证明在使用注释样式的 AspectJ 时通常需要使用完全限定的类名(在本机语法导入中就足够了).
  • Your Writer is already a Thread subclass, no need to wrap it into another thread instance. (I know you probably just did it for being able to name the thread, but that could have been achieved by adding a constructor in your class taking the name argument and passing it through to the super class constructor.)
  • You should not call a variable of type JoinPoint pointcut because a joinpoint is not a pointcut AOP-wise.
  • I factored out the logging into its own helper method and improved it a bit so we can see more clearly what happens when.
  • I decided to replace each pair of before and after advice by an around advice. This is optional, of course, but I prefer to see the control flow in one place in this case. BTW, be careful to change the around advice's return type to Object and actually return something if you want to target non-void methods. Here it is not necessary because in both cases we have void methods.
  • I also decided to inline the pointcuts, which is also optional, but makes the sample code somewhat more concise for demonstration purposes here.
  • I added a Reader class and use it in order to show the difference between reentrant read vs write locks.
  • I also took care of making MyStructure instances nameable and printable in order to identify the target objects more easily in the log.
  • I randomised the execution order of reader/writer threads so as to mix them in a more real-world fashion. In order to avoid exceptions polluting the log when reading from a newly created MyStructure before writing into, I made sure that MyStructure gets a default element right in the constructor. I did not want to catch exceptions here to in order to keep the sample code simple.
  • I put the aspect in another package than the application code in order to demonstrate than generally you need to use fully qualified class names when using annotation-style AspectJ (in native syntax imports would be enough).

现在有什么解决办法?基本上就是这样,因为上面提到的更改只会使代码更好或测试程序更接近现实情况:

Now what is the solution? Basically just this, because the changes mentioned above just make the code better or the test program closer to real-life situations:

@Aspect("pertarget(execution(de.scrum_master.app.MyStructure.new(..)))")
public class LocksAspect { // (...)

这会为每个 MyStructure 对象创建一个方面实例.这也是为什么我们可以直接分配 readWriteLockreadLockwriteLock 的值,而不是像在单例中那样使用特殊的切入点 + 建议对方面.

This creates one aspect instance per MyStructure object. This is also why we can assign the values of readWriteLock, readLock and writeLock directly instead of using a special pointcut + advice pair like in your singleton aspect.

这是完整的重构示例代码:

Here is the full, refactored sample code:

应用代码+驱动应用:

package de.scrum_master.app;

import java.util.ArrayList;
import java.util.List;

public class MyStructure {
  private String name;
  private List<String> myList;

  public MyStructure(String name) {
    this.name = name;
    myList = new ArrayList<String>();
    myList.add("dummy element to permit reading");
  }

  public void insert(String value) {
    myList.add(value);
  }

  public void read(int pos) {
    myList.get(pos);
  }

  @Override
  public String toString() {
    return "MyStructure[" + name + "]";
  }
}

package de.scrum_master.app;

public class Writer extends Thread {
  private MyStructure myStructure;

  public Writer(MyStructure myStructure) {
    this.myStructure = myStructure;
  }

  @Override
  public void run() {
    myStructure.insert("example");
  }
}

package de.scrum_master.app;

public class Reader extends Thread {
  private MyStructure myStructure;

  public Reader(MyStructure myStructure) {
    this.myStructure = myStructure;
  }

  @Override
  public void run() {
    myStructure.read(0);
  }
}

package de.scrum_master.app;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Application {
  public static void main(String[] args) {
    MyStructure structureA = new MyStructure("One");
    MyStructure structureB = new MyStructure("Two");
    List<Thread> threads = Arrays.asList(
      new Writer(structureA), new Writer(structureB), new Writer(structureA), new Writer(structureB),
      new Reader(structureA), new Reader(structureB), new Reader(structureA), new Reader(structureB),
      new Reader(structureA), new Reader(structureB), new Reader(structureA), new Reader(structureB)
    );
    Collections.shuffle(threads);
    for (Thread thread : threads)
      thread.start();
  }
}

方面:

package de.scrum_master.aspect;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import de.scrum_master.app.MyStructure;

@Aspect("pertarget(execution(de.scrum_master.app.MyStructure.new(..)))")
public class LocksAspect {
  private static final long startTime = System.currentTimeMillis();

  private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  private Lock readLock = readWriteLock.readLock();
  private Lock writeLock = readWriteLock.writeLock();

  @Around("target(myStructure) && execution(void insert(String))")
  public void InsertPointcutBefore(ProceedingJoinPoint thisJoinPoint, MyStructure myStructure) throws Throwable {
    writeLock.lock();
    log("entering write section", myStructure);
    try {
      Thread.sleep(1000);
      thisJoinPoint.proceed();
    } finally {
      log("exiting write section", myStructure);
      writeLock.unlock();
    }
  }

  @Around("target(myStructure) && execution(void read(int))")
  public void ReadPointcutBefore(ProceedingJoinPoint thisJoinPoint, MyStructure myStructure) throws Throwable {
    readLock.lock();
    log("entering read section", myStructure);
    try {
      Thread.sleep(1000);
      thisJoinPoint.proceed();
    } finally {
      log("exiting read section", myStructure);
      readLock.unlock();
    }
  }

  private static void log(String message, Object targetObject)  {
    System.out.printf(
      "%8d ms | %-25s | %-17s | %s%n",
      System.currentTimeMillis() - startTime,
      Thread.currentThread(),
      targetObject,
      message
    );
  }
}

示例日志输出:

       4 ms | Thread[Thread-3,5,main]   | MyStructure[Two]  | entering write section
       4 ms | Thread[Thread-6,5,main]   | MyStructure[One]  | entering read section
       4 ms | Thread[Thread-8,5,main]   | MyStructure[One]  | entering read section
       4 ms | Thread[Thread-4,5,main]   | MyStructure[One]  | entering read section
       4 ms | Thread[Thread-10,5,main]  | MyStructure[One]  | entering read section
    1019 ms | Thread[Thread-3,5,main]   | MyStructure[Two]  | exiting write section
    1020 ms | Thread[Thread-8,5,main]   | MyStructure[One]  | exiting read section
    1020 ms | Thread[Thread-4,5,main]   | MyStructure[One]  | exiting read section
    1020 ms | Thread[Thread-11,5,main]  | MyStructure[Two]  | entering read section
    1020 ms | Thread[Thread-5,5,main]   | MyStructure[Two]  | entering read section
    1020 ms | Thread[Thread-6,5,main]   | MyStructure[One]  | exiting read section
    1020 ms | Thread[Thread-10,5,main]  | MyStructure[One]  | exiting read section
    1025 ms | Thread[Thread-2,5,main]   | MyStructure[One]  | entering write section
    2023 ms | Thread[Thread-11,5,main]  | MyStructure[Two]  | exiting read section
    2024 ms | Thread[Thread-5,5,main]   | MyStructure[Two]  | exiting read section
    2025 ms | Thread[Thread-1,5,main]   | MyStructure[Two]  | entering write section
    2026 ms | Thread[Thread-2,5,main]   | MyStructure[One]  | exiting write section
    2026 ms | Thread[Thread-0,5,main]   | MyStructure[One]  | entering write section
    3026 ms | Thread[Thread-1,5,main]   | MyStructure[Two]  | exiting write section
    3026 ms | Thread[Thread-7,5,main]   | MyStructure[Two]  | entering read section
    3026 ms | Thread[Thread-9,5,main]   | MyStructure[Two]  | entering read section
    3028 ms | Thread[Thread-0,5,main]   | MyStructure[One]  | exiting write section
    4028 ms | Thread[Thread-7,5,main]   | MyStructure[Two]  | exiting read section
    4029 ms | Thread[Thread-9,5,main]   | MyStructure[Two]  | exiting read section

这篇关于带有 AspectJ 切入点的 ReentrantReadWriteLock 用于每个初始化类型的 MyStructure的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆