single thread executor

Ron Pressler ron.pressler at oracle.com
Mon Sep 7 11:39:44 UTC 2020


There are some circumstances in which you may want a custom scheduler, but
InetAddress.getByName isn’t one of them. Occasionally blocking the OS thread
is fine; it should only be a problem if done *very* frequently. The work-stealing
scheduler should automatically smooth over such issues (and in the case of 
getByName it might even automatically increase the size of the worker thread
pool). If it doesn’t, and you see some cases where throughput is unsatisfactory, 
please report them to this list. Custom schedulers are not the solution for
such cases, certainly not by default. They should be reached for only in much
more "special" circumstances.

— Ron


On 7 September 2020 at 11:57:53, Miguel Ping (miguel.ping at gmail.com(mailto:miguel.ping at gmail.com)) wrote:

> Thanks all,
>  
> I understand that by principle you won't want to do custom scheduling, because virtual threads will be able to suspend at specified points and free the carrier thread to be scheduled with another task.  
> I do like to understand the limits of loom by experimenting; eg: according to the docs, getByName pin the carrier threads (at least right now), so if by any reason my service is heavy on those calls I might want to schedule that on dedicated threads.
>  
>  
> -- Miguel
>  
>  
> On Sun, Sep 6, 2020 at 11:28 AM Ron Pressler wrote:
> > > in a lot of cases you probably won't have to worry about carrier threads
> > > since it is already set up for you.
> >  
> > Exactly! A custom virtual thread scheduler is an advanced feature that should  
> > only be used in special circumstances.
> >  
> > - Ron
> >  
> >  
> >  
> > On 6 September 2020 at 09:15:29, Michael Bien (mbien42 at gmail.com(mailto:mbien42 at gmail.com)(mailto:mbien42 at gmail.com)) wrote:
> >  
> > > it might help to think of it being two layers. You have plain old java
> > > threads which map to OS threads ("real" threads) and the new virtual
> > > threads.
> > >  
> > > Virtual threads can only make progress when they are mounted on carrier
> > > threads, which are "real" threads. They add a nice feature however:
> > > concurrent waiting (for resources) without blocking a carrier - which is
> > > a big deal.
> > >  
> > > to setup everything, carriers + virtuals:
> > >  
> > > |// plain old thread factory and thread pool using the new builder
> > > ThreadFactory carrierTF = Thread.builder().name("carrier#",
> > > 0).factory(); ExecutorService carrierPool =
> > > Executors.newFixedThreadPool( CARRIER_THREAD_COUNT, carrierTF);|
> > >  
> > > |// factory for virtual threads scheduled on the carrier pool
> > > ThreadFactory virtualTF = Thread.builder() .virtual(carrierPool)
> > > .name("virtual#", 0).factory(); // thread executor will spawn a new
> > > virtual thread for each task |||ExecutorService |executor = Executors.newThreadExecutor(virtualTF);|
> > >  
> > >  
> > >  
> > > in a lot of cases you probably won't have to worry about carrier threads
> > > since it is already set up for you.
> > > for example this will schedule a new virtual thread on the default
> > > scheduler, which is a ForkJoinPool.
> > >  
> > > |Thread.startVirtualThread(() -> { System.out.println("Hello Loom from
> > > "+Thread.currentThread()+"!"); });|
> > >  
> > >  
> > > hope this helps a bit,
> > >  
> > > michael
> > >  
> > >  
> > > - - -
> > > mbien.dev(https://urldefense.com/v3/__http://mbien.dev__;!!GqivPVa7Brio!KrI98xLqw3kO7ndn7Fbh-IJAcU6MKbRno2netg4swupPC5G_J1tANjD4_TxmIVwlxA$)
> > >  
> > > On 05.09.20 20:14, Miguel Ping wrote:
> > > > Yeah I was about to reply exactly this.
> > > >
> > > > I always thought I knew basic executors, but now I find the api confusing:
> > > > I create a ThreadExecutor that runs off a ThreadFactory (so far so good)
> > > > that is built using virtual threads using a singleThreadExecutor.
> > > > It's a bit confusing to see two executors here, but I guess the
> > > > newThreadExecutor is just wrapping the threadFactory.
> > > >
> > > > Anyway, thanks alot for replying, especially on a saturday.
> > > > I'm anxiously looking forward to continuing experimenting with Loom.
> > > >
> > > > Have a nice weekend.
> > > >
> > > > On Sat, Sep 5, 2020 at 6:02 PM Ron Pressler wrote:
> > > >
> > > >> Correction:
> > > >>
> > > >> It should be Executors.newThreadExecutor(tf) rather than
> > > >> Executors.newUnboundedExecutor(tf).
> > > >> The latter was the old API method, which has been renamed to the former.
> > > >>
> > > >> — Ron
> > > >>
> > > >>
> > > >> On 4 September 2020 at 18:50:27, Ron Pressler (ron.pressler at oracle.com(mailto:ron.pressler at oracle.com))
> > > >> wrote:
> > > >>
> > > >> Remember, you want multiple virtual threads, but use only on platform
> > > >> thread to schedule them. So you need to pass the single-thread executor
> > > >> as the virtual thread scheduler:
> > > >>
> > > >> ThreadFactory tf =
> > > >> Thread.builder().virtual(Executors.newSingleThreadExecutor()).factory();
> > > >>
> > > >> And then you can use the thread factory directly to create virtual
> > > >> threads,
> > > >> or use it like so:
> > > >>
> > > >> ExecutorService e = Executors.newUnboundedExecutor(tf);
> > > >>
> > > >> - Ron
> > > >>
> > > >>
> > > >> On 4 September 2020 at 18:21:45, Miguel Ping (miguel.ping at gmail.com(mailto:miguel.ping at gmail.com))
> > > >> wrote:
> > > >>
> > > >> Hi all,
> > > >>
> > > >> I was experimenting with single thread executor and loom, and I was
> > > >> expecting that if I have two tasks where the first call goes through
> > > >> Socket.read, loom would schedule the second one. I'm pretty sure I'm doing
> > > >> something wrong.
> > > >>
> > > >> Here's the output I got (I was expecting "Hello world" to appear before
> > > >> "Connected!"; the program only concludes after I Ctrl+C the netcat
> > > >> process):
> > > >>
> > > >>
> > > >> $ nc -p 5555 -kl
> > > >>
> > > >> -- output --
> > > >> sleeping 1s
> > > >> Connected!
> > > >> Hello world
> > > >>
> > > >> -- java --
> > > >>
> > > >> import java.net.InetSocketAddress;
> > > >> import java.nio.ByteBuffer;
> > > >> import java.nio.channels.SocketChannel;
> > > >> import java.util.Arrays;
> > > >> import java.util.concurrent.Callable;
> > > >> import java.util.concurrent.Executors;
> > > >>
> > > >> public class Test {
> > > >>
> > > >> public static String block() {
> > > >> // nc -p 5555 -kl
> > > >> try {
> > > >> System.out.println("sleeping 1s");
> > > >> Thread.sleep(1000);
> > > >> var socket = SocketChannel.open();
> > > >> socket.connect(new InetSocketAddress("localhost", 5555));
> > > >> System.out.println("Connected!");
> > > >> socket.read(ByteBuffer.allocate(10)); // I was hoping this would allow the
> > > >> other task to be scheduled
> > > >> return "End";
> > > >> } catch (Exception e) {
> > > >> e.printStackTrace();
> > > >> return "Failed";
> > > >> }
> > > >> }
> > > >>
> > > >> public static String print() {
> > > >> System.out.println("Hello world");
> > > >> return "Printed";
> > > >> }
> > > >>
> > > >> public static void main(String[] args) throws Throwable {
> > > >> var e = Executors.newFixedThreadPool(1,
> > > >> Thread.builder().virtual().factory());
> > > >> Callable t1 = Test::block;
> > > >> Callable t2 = Test::print;
> > > >>
> > > >> var tasks = Arrays.asList(t1, t2);
> > > >> var fut = e.invokeAll(tasks);
> > > >> }
> > > >> }
> > > >>
> > > >> --
> > > >> Thanks
> > > >>
> > > >>
> > >  
> >  



More information about the loom-dev mailing list