001/** 002 * The MIT License (MIT) 003 * 004 * Copyright (c) 2018 nobark (tools4j), Marco Terzer, Anton Anufriev 005 * 006 * Permission is hereby granted, free of charge, to any person obtaining a copy 007 * of this software and associated documentation files (the "Software"), to deal 008 * in the Software without restriction, including without limitation the rights 009 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 010 * copies of the Software, and to permit persons to whom the Software is 011 * furnished to do so, subject to the following conditions: 012 * 013 * The above copyright notice and this permission notice shall be included in all 014 * copies or substantial portions of the Software. 015 * 016 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 017 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 018 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 019 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 020 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 021 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 022 * SOFTWARE. 023 */ 024package org.tools4j.nobark.loop; 025 026import sun.misc.Contended; 027 028import java.util.Objects; 029import java.util.concurrent.ThreadFactory; 030import java.util.function.BooleanSupplier; 031import java.util.function.Function; 032 033/** 034 * A thread that performs a {@link java.lang.Runnable runnable} in a new thread. 035 * The thread is started immediately upon construction and it can be stopped via stop or auto-close method. 036 */ 037public class StoppableThread implements Stoppable { 038 private final Function<BooleanSupplier, Runnable> runnableFactory; 039 private final Thread thread; 040 @Contended 041 private volatile boolean running; 042 043 /** 044 * Constructor for stoppable thread; it is recommended to use the static start(..) methods instead. 045 * 046 * @param runnableFactory the factory for the runnable; 047 * the <i>{@link #isRunning}</i> condition is passed to the factory as lambda 048 * @param threadFactory the factory to provide the thread 049 */ 050 protected StoppableThread(final Function<BooleanSupplier, Runnable> runnableFactory, 051 final ThreadFactory threadFactory) { 052 this.thread = threadFactory.newThread(this::run); 053 this.runnableFactory = Objects.requireNonNull(runnableFactory); 054 this.running = true; 055 thread.start(); 056 } 057 058 /** 059 * Creates, starts and returns a new shutdownable thread. 060 * 061 * @param runnableFactory the factory for the runnable; 062 * the <i>{@link #isRunning}</i> condition is passed to the factory as lambda 063 * @param threadFactory the factory to provide the thread 064 * @return the newly created and started stoppable thread 065 */ 066 public static StoppableThread start(final Function<BooleanSupplier, Runnable> runnableFactory, 067 final ThreadFactory threadFactory) { 068 return new StoppableThread(runnableFactory, threadFactory); 069 } 070 071 private void run() { 072 final Runnable runnable = runnableFactory.apply(this::isRunning); 073 runnable.run(); 074 } 075 076 public boolean isRunning() { 077 return running; 078 } 079 080 @Override 081 public void stop() { 082 running = false; 083 } 084 085 public void join() { 086 join(0); 087 } 088 089 public void join(final long millis) { 090 try { 091 thread.join(millis); 092 } catch (final InterruptedException e) { 093 throw new IllegalStateException("join was interrupted for thread=" + thread); 094 } 095 } 096 097 @Override 098 public String toString() { 099 return thread.getName(); 100 } 101}