1515 */
1616package io .github .ascopes .jct .utils ;
1717
18+ import java .util .concurrent .locks .Lock ;
19+ import java .util .concurrent .locks .ReentrantLock ;
1820import java .util .Objects ;
1921import java .util .function .Supplier ;
2022import org .apiguardian .api .API ;
3335 * <p>This descriptor is thread-safe. No guarantees are made about the thread-safety of the
3436 * internally stored data, nor the initializer supplier.
3537 *
36- * <p>This descriptor currently does not support virtual threads. Any synchronization will
37- * occur on the physical thread level.
38+ * <p>This descriptor supports working with Loom virtual threads correctly.
3839 *
3940 * @param <T> the type of lazy value to return when accessed.
4041 * @author Ashley Scopes
4445public final class Lazy <T > {
4546
4647 private final Supplier <T > initializer ;
47- private final Object lock ;
48+ private final Lock lock ;
4849 private volatile @ Nullable T data ;
4950
5051 /**
@@ -54,7 +55,7 @@ public final class Lazy<T> {
5455 */
5556 public Lazy (Supplier <T > initializer ) {
5657 this .initializer = Objects .requireNonNull (initializer , "initializer must not be null" );
57- lock = new Object ();
58+ lock = new ReentrantLock ();
5859 data = null ;
5960 }
6061
@@ -65,10 +66,13 @@ public String toString() {
6566
6667 // Synchronize to prevent a race condition between reading
6768 // the data and reading the "initialized" flag.
68- synchronized (lock ) {
69+ lock .lock ();
70+ try {
6971 repr = new ToStringBuilder (this )
7072 .attribute ("data" , data )
7173 .toString ();
74+ } finally {
75+ lock .unlock ();
7276 }
7377
7478 return repr ;
@@ -81,10 +85,13 @@ public String toString() {
8185 */
8286 public T access () {
8387 if (data == null ) {
84- synchronized (lock ) {
88+ lock .lock ();
89+ try {
8590 if (data == null ) {
8691 data = initializer .get ();
8792 }
93+ } finally {
94+ lock .unlock ();
8895 }
8996 }
9097
@@ -101,8 +108,11 @@ public T access() {
101108 */
102109 public void destroy () {
103110 if (data != null ) {
104- synchronized (lock ) {
111+ lock .lock ();
112+ try {
105113 data = null ;
114+ } finally {
115+ lock .unlock ();
106116 }
107117 }
108118 }
@@ -120,10 +130,13 @@ public <E extends Throwable> Lazy<T> ifInitialized(
120130 ThrowingConsumer <? super T , ? extends E > consumer
121131 ) throws E {
122132 if (data != null ) {
123- synchronized (lock ) {
133+ lock .lock ();
134+ try {
124135 if (data != null ) {
125136 consumer .consume (data );
126137 }
138+ } finally {
139+ lock .unlock ();
127140 }
128141 }
129142
0 commit comments