Несколько дней назад я просматривал Интернет и нашел интересный фреймворк с открытым исходным кодом под названием CheckThread . Это инструмент статического анализа для выявления ошибок параллелизма Java во время компиляции. Инструменты статического анализа используются для выявления ошибок программирования в коде путем анализа их байтового кода. Для меня инструмент, нацеленный на обнаружение ошибок параллелизма во время компиляции, стоил потратить время на него. Итак, я решил поиграть с checkthread, чтобы узнать его возможности.
CheckThread требует от разработчиков указывать политику потоков в xml или аннотациях. Политика потоков определяет, является ли фрагмент кода (т. Е. Метод) поточно-безопасным, не поточно-безопасным или ограниченным потоком. Ограниченный поток означает, что этот метод ограничен определенным потоком времени выполнения. Например, Swing API должен быть вызван в потоке Event-Dispatch.
необходимое условие
Прежде чем ты начнешь:
- Скачать Eclipse Plugin . Поместите плагин в jar в папку плагинов Eclipse и перезапустите Eclipse. Для получения дополнительной информации обратитесь сюда .
- Скачать Checkthread с аннотацией jar . Этот jar-файл отсутствует в любом репозитории maven, поэтому установите его вручную в свой репозиторий maven.
mvn install:install-file -Dfile=checkthread-annotations-1.0.9.jar -DgroupId=org.checkthread -DartifactId=checkthread-annotations -Dversion=1.0.9 -Dpackaging=jar
Возможности CheckThread
Давайте посмотрим на пример:
public class ThreadSafetyExample {
final Map<String, String> helperMap = new HashMap<String, String>();
public void addElementToMap() {
helperMap.put("name", "shekhar");
}
}
Это простой класс, который помещает пару ключ-значение в карту. Этот поток кода безопасен? Нет.
Давайте проверим этот класс, используя несколько потоков. В модульном тесте я использую CountDownLatch для получения максимального параллелизма. Я говорил о CountDownLatch в предыдущей статье .
@Test
public void regressionTest() throws Exception{
for (int i = 1; i <= 100; i++) {
System.out.println("Runing "+i);
addElementToMapWhenAccessedByMultipleThread();
}
}
public void addElementToMapWhenAccessedByMultipleThread() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
final ThreadSafetyExample helper = new ThreadSafetyExample();
class MyThread extends Thread {
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
}
helper.addElementToMap();
}
}
int threadCount = 2000;
MyThread[] myThreads = new MyThread[threadCount];
for (int i = 0; i < myThreads.length; i++) {
myThreads[i] = new MyThread();
myThreads[i].start();
}
latch.countDown();
for (MyThread myThread : myThreads) {
myThread.join();
}
assertEquals(1, helper.helperMap.size());
}
I am calling the method addElementToMapWhenAccessedByMultipleThread in a for loop because if you run the test only once it might not fail (Thread timing). So, how can these types of errors be detected at compile time if most of the unit-tests and integration tests that we write do not test the code in multi-threaded environments? CheckThread can help you out in such situations. CheckThread can only help you if you annotate your method with ThreadSafe annotation. For example, lets apply @ThreadSafe annotation to the ThreadSafetyExample class:
public class ThreadSafetyExample {
final Map<String, String> helperMap = new HashMap<String, String>();
@ThreadSafe
public void addElementToMap() {
helperMap.put("name", "shekhar");
}
}
Now when you run the CheckThread by pressing the button you will see a compile time error as shown below:
As you can see in the above image, the tool shows the code where problems exist and descriptions of errors in the problems section. This can come in handy while writing multi threaded code. You just need to think about your contract, whether the method you have written should be thread safe or not.
This was just a simple use case but it can also help you detect race conditions. Have a look at this tool, maybe it can help you detect concurrency bugs.