ReentrantReadWriteLock с AspectJ pointcut для каждого инициализированного типа MyStructure - PullRequest
0 голосов
/ 13 ноября 2018

Я изо всех сил пытаюсь создать ReentrantReadWriteLock с AspectJ для каждого отдельного объекта, который построен и является типом Mystructure. Вот мой исходный код.

Класс аспекта

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();
    }
}

Класс Writer Thread. (Класс потока Reader не важен, потому что у меня другая проблема, поэтому я ее пропустил)

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 {} 

Теперь моя проблема в том, как я получу новый ReentrantReadWriteLock для каждого созданного объекта Mystructure. Например, если мы запустим приведенный выше пример, и Thread1, и Thread2 должны иметь доступ к критическому разделу, поскольку они имеют разные ссылки на объект, но этого не должно было произойти. Моя проблема в том, что Thread2 будет блокировать и ждать завершения Thread1, что неправильно. Как я могу обойти эту проблему строительства с Aspect4j?

1 Ответ

0 голосов
/ 22 ноября 2018

Ключом к решению вашей проблемы является то, что вам нужен один набор блокировок для MyStructure экземпляра.Ваш аспект, однако, одиночен.Поэтому вам нужно либо использовать другую схему создания экземпляров аспектов (именно это я и буду использовать в своем ответе), либо вести ручную бухгалтерию в рамках одноэлементного аспекта, сохраняя набор блокировок и добавляя новый элемент в этот набор всякий раз, когда объект MyStructureсоздан.

Чтобы лучше понять мой ответ, пожалуйста, обратитесь к руководству AspectJ за информацией о создании экземпляров .

Прежде чем мы начнем, несколько комментариев относительно вашего кодаи почему я немного его изменил:

  • Ваш Writer уже является подклассом Thread, нет необходимости заключать его в другой экземпляр потока.(Я знаю, что вы, вероятно, просто сделали это для того, чтобы иметь возможность именовать поток, но этого можно было бы достичь, добавив в ваш класс конструктор, принимающий аргумент name и передавший его конструктору суперкласса.)
  • Вы не должны вызывать переменную типа JoinPoint pointcut, потому что точка соединения не является точечной с точки зрения AOP.
  • Я учел логирование в его собственном вспомогательном методе и немного его улучшил, чтобы мы могли видетьболее ясно, что происходит, когда.
  • Я решил заменить каждую пару до и после совета на общий совет.Это необязательно, конечно, но я предпочитаю видеть поток управления в одном месте в этом случае.Кстати, будьте осторожны, чтобы изменить тип возвращаемого значения для советника на Object и на самом деле вернуть что-то, если вы хотите использовать целевые методы.Здесь это не является необходимым, потому что в обоих случаях у нас есть пустые методы.
  • Я также решил встроить pointcuts, что также необязательно, но делает пример кода несколько более кратким для демонстрационных целей.
  • Я добавил класс Reader и использую его для того, чтобы показать разницу между повторным входящим чтением и блокировками записи.
  • Я также позаботился о том, чтобы MyStructure экземпляров можно было именовать и печатать, чтобы определить цельобъекты в журнале легче.
  • Я рандомизировал порядок выполнения потоков чтения / записи, чтобы смешивать их в более реальном мире.Чтобы избежать исключений, загрязняющих журнал при чтении из только что созданного MyStructure перед записью в него, я убедился, что MyStructure получает элемент по умолчанию прямо в конструкторе.Я не хотел перехватывать здесь исключения для упрощения примера кода.
  • Я поместил аспект в другой пакет, чем код приложения, чтобы продемонстрировать, чем обычно нужно использовать полные имена классов, когдаиспользование AspectJ в стиле аннотаций (в собственном синтаксисе будет достаточно импорта).

Теперь, каково решение?В основном именно это, потому что упомянутые выше изменения только улучшают код или тестовую программу ближе к реальным ситуациям:

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

Это создает один экземпляр аспекта для MyStructure объекта.Именно поэтому мы можем назначить значения readWriteLock, readLock и writeLock напрямую, а не использовать специальную пару pointcut + advice, как в вашем одноэлементном аспекте.

Вот полный рефакторизованный примеркод:

Код приложения + приложение драйвера:

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
...