🖥 Scoped Values в Java (Часть 1)



Судя по реакциям на предыдущих постах про JEP, они вам нравятся, так что продолжаем разбирать свежые JEP'ы.



Представим себе небольшой веб-фреймворк, который обрабатывает HTTP-запросы. Фреймворк создает контекст для каждого запроса и передает его через методы:





@Override

public void handle(Request request, Response response) {

var userInfo = readUserInfo();

}



private UserInfo readUserInfo() {

return (UserInfo) framework.readKey("userInfo", context);

}





Ранее для передачи контекста использовались переменные типа ThreadLocal:





private final static ThreadLocal<FrameworkContext> CONTEXT = new ThreadLocal<>();



void serve(Request request, Response response) {

var context = createContext(request);

CONTEXT.set(context);

Application.handle(request, response);

}



public PersistedObject readKey(String key) {

var context = CONTEXT.get();

var db = getDBConnection(context);

db.readKey(key);

}





Scoped Values API впервые был представлен в JDK 20 и прошел несколько итераций и улучшений в последующих версиях JDK (JEP 429, JEP 446, JEP 464, JEP 481). Scoped Values представляют собой новый механизм для передачи неизменяемых данных между методами в одном потоке и между дочерними потоками. Этот механизм проще в использовании по сравнению с ThreadLocal и обладает меньшими затратами по времени и памяти.



У подхода, использующего ThreadLocal, есть несколько недостатков:



1. Неограниченная изменяемость — переменные могут изменяться в любое время любым кодом в потоке

2. Неограниченное время жизни — переменные могут существовать дольше, чем необходимо, что может приводить к утечкам памяти

3. Высокая стоимость наследования — при создании дочерних потоков переменные должны копироваться, что увеличивает затраты по памяти



Scoped Values позволяют избежать этих проблем, обеспечивая одноразовую запись и ограниченное время жизни значений:





final static ScopedValue<FrameworkContext> CONTEXT = ScopedValue.newInstance();



void serve(Request request, Response response) {

var context = createContext(request);

ScopedValue.runWhere(CONTEXT, context, () -> Application.handle(request, response));

}



public PersistedObject readKey(String key) {

var context = CONTEXT.get();

var db = getDBConnection(context);

db.readKey(key);

}





В данном случае метод ScopedValue.runWhere связывает значение с текущим потоком на время выполнения лямбда-выражения, после чего связь уничтожается, что улучшает производительность и безопасность кода.



В 23 версии Java фича будет пересмотрена повторно с одним изменением: тип параметра операции метода ScopedValue.callWhere будет являться новым функциональным интерфейсом, который позволит компилятору Java делать вывод о том, может ли быть выброшено проверяемое исключение. Подробнее про ScopedValue.callWhere и улучшения, связанные с ним, поговорим во второй части.



Ставьте 🔥 если хотите вторую часть!



#Java #JEP