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

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

问题描述

我正在努力为每个构造好的Mystruct类型的单个对象使用AspectJ创建一个ReentrantReadWriteLock.这是我的源代码.

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.

为了更好地理解我的答案,请参阅AspectJ手册,以获取有关

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建议的返回类型更改为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

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

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