Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8326510

Why not join ThreadLocal and ScopedValue

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not an Issue
    • Icon: P4 P4
    • None
    • None
    • core-libs

      ADDITIONAL SYSTEM INFORMATION :
      Java 21 ff

      A DESCRIPTION OF THE PROBLEM :
      ... to extend the use of ThreadLocal to the new world of virtual threads and
      scoped and structured concurrency.

      The simple idea behind:

        let ThreadLocals use scoped values if bound, else fallback to normal mode

      A new class or change/extend ThreadLocal-class itself:

        public class ScopedThreadLocal<T>
          extends ThreadLocal<T> {

          public static <S> ScopedThreadLocal<S> withInitial(
            final Supplier<? extends S> supplier) {
            return new ScopedThreadLocal<>() {
              @Override
              protected S initialValue() {
                return supplier.get();
              }
            };
          }

          private final ScopedValue<T> scopedValue;

          public ScopedThreadLocal() {
            super();
            this.scopedValue = ScopedValue.newInstance();
          }

          public ScopedValue<T> getScopedValue() {
            return scopedValue;
          }

          @Override
          public T get() {
            if (scopedValue.isBound()) {
              return scopedValue.get();
            }
            return super.get();
          }
        }

      First test looks promising:

        static class CNT {
          private static final AtomicInteger cnt = new AtomicInteger();
          final String nam;
          final int num;

          CNT(final String name) {
            super();
            nam = Objects.requireNonNullElse(name, "CNT");
            num = cnt.incrementAndGet();
          }

          public CNT() {
            this(null);
          }

          String getName() {
            return nam;
          }

          int getNum() {
            return num;
          }

          @Override
          public String toString() {
            return "%1$s[%2$d]".formatted(nam, Integer.valueOf(num));
          }
        }

        private static final ScopedThreadLocal<CNT> STL =
          ScopedThreadLocal.withInitial(() -> new CNT());

        private static ExecutorService es(final boolean virtual) {
          if (virtual)
            return Executors.newVirtualThreadPerTaskExecutor();
          return Executors.newFixedThreadPool(5);
        }

        static void testScopedThreadLocal() {
          final boolean virtual = "1".equals("1"); // --- change testmode
          final boolean scoped = "1".equals("1"); // --- change testmode
          final int tcnt = 50;
          try (final ExecutorService es = es(virtual);) {
            final Runnable r = () -> {
              final Long tid = Long.valueOf(Thread.currentThread().threadId());
              System.out.printf("main-thread %d: %s%n", tid, STL.get());
              try (var scope = new StructuredTaskScope<>();) {
                IntStream.range(0, 5).forEach(i -> {
                  scope.fork(() -> System.out.printf(" sub-thread %d / %d: %s%n",
                    tid, Integer.valueOf(i), STL.get()));
                });
              }
            };
            for (int i = 0; i < tcnt; i++) {
              if (scoped)
                es.submit(
                  () -> ScopedValue.where(STL.getScopedValue(), new CNT()).run(r));
              else
                es.submit(r);
            }
          }
          System.out.println("instances used: " + CNT.cnt.get());
        }

        public static void main(final String[] args) {
          testScopedThreadLocal();
        }


      Merry christmas!

      Kurt



            Unassigned Unassigned
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: