All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.ttl.TtlRunnable Maven / Gradle / Ivy

package com.alibaba.ttl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import static com.alibaba.ttl.TransmittableThreadLocal.Transmitter.*;

/**
 * {@link TtlRunnable} decorate {@link Runnable}, so as to get {@link TransmittableThreadLocal}
 * and transmit it to the time of {@link Runnable} execution, needed when use {@link Runnable} to thread pool.
 * 

* Use factory methods {@link #get} / {@link #gets} to create instance. * * @author Jerry Lee (oldratlee at gmail dot com) * @see com.alibaba.ttl.threadpool.TtlExecutors * @see java.util.concurrent.Executor * @see java.util.concurrent.ExecutorService * @see java.util.concurrent.ThreadPoolExecutor * @see java.util.concurrent.ScheduledThreadPoolExecutor * @see java.util.concurrent.Executors * @since 0.9.0 */ public final class TtlRunnable implements Runnable { private final AtomicReference capturedRef; private final Runnable runnable; private final boolean releaseTtlValueReferenceAfterRun; private TtlRunnable(Runnable runnable, boolean releaseTtlValueReferenceAfterRun) { this.capturedRef = new AtomicReference(capture()); this.runnable = runnable; this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun; } /** * wrap method {@link Runnable#run()}. */ @Override public void run() { Object captured = capturedRef.get(); if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) { throw new IllegalStateException("TTL value reference is released after run!"); } Object backup = replay(captured); try { runnable.run(); } finally { restore(backup); } } /** * return original/unwrapped {@link Runnable}. */ public Runnable getRunnable() { return runnable; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TtlRunnable that = (TtlRunnable) o; return runnable.equals(that.runnable); } @Override public int hashCode() { return runnable.hashCode(); } @Override public String toString() { return this.getClass().getName() + " - " + runnable.toString(); } /** * Factory method, wrapper input {@link Runnable} to {@link TtlRunnable}. * * @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}. * @return Wrapped {@link Runnable} * @throws IllegalStateException when input is {@link TtlRunnable} already. */ public static TtlRunnable get(Runnable runnable) { return get(runnable, false, false); } /** * Factory method, wrapper input {@link Runnable} to {@link TtlRunnable}. * * @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}. * @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred. * @return Wrapped {@link Runnable} * @throws IllegalStateException when input is {@link TtlRunnable} already. */ public static TtlRunnable get(Runnable runnable, boolean releaseTtlValueReferenceAfterRun) { return get(runnable, releaseTtlValueReferenceAfterRun, false); } /** * Factory method, wrapper input {@link Runnable} to {@link TtlRunnable}. * * @param runnable input {@link Runnable}. if input is {@code null}, return {@code null}. * @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred. * @param idempotent is idempotent mode or not. if {@code true}, just return input {@link Runnable} when it's {@link TtlRunnable}, * otherwise throw {@link IllegalStateException}. * Caution: {@code true} will cover up bugs! DO NOT set, only when you know why. * @return Wrapped {@link Runnable} * @throws IllegalStateException when input is {@link TtlRunnable} already and not idempotent. */ public static TtlRunnable get(Runnable runnable, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) { if (null == runnable) { return null; } if (runnable instanceof TtlRunnable) { if (idempotent) { // avoid redundant decoration, and ensure idempotency return (TtlRunnable) runnable; } else { throw new IllegalStateException("Already TtlRunnable!"); } } return new TtlRunnable(runnable, releaseTtlValueReferenceAfterRun); } /** * wrapper input {@link Runnable} Collection to {@link TtlRunnable} Collection. * * @param tasks task to be wrapped. if input is {@code null}, return {@code null}. * @return wrapped tasks * @throws IllegalStateException when input is {@link TtlRunnable} already. */ public static List gets(Collection tasks) { return gets(tasks, false, false); } /** * wrapper input {@link Runnable} Collection to {@link TtlRunnable} Collection. * * @param tasks task to be wrapped. if input is {@code null}, return {@code null}. * @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred. * @return wrapped tasks * @throws IllegalStateException when input is {@link TtlRunnable} already. */ public static List gets(Collection tasks, boolean releaseTtlValueReferenceAfterRun) { return gets(tasks, releaseTtlValueReferenceAfterRun, false); } /** * wrapper input {@link Runnable} Collection to {@link TtlRunnable} Collection. * * @param tasks task to be wrapped. if input is {@code null}, return {@code null}. * @param releaseTtlValueReferenceAfterRun release TTL value reference after run, avoid memory leak even if {@link TtlRunnable} is referred. * @param idempotent is idempotent mode or not. if {@code true}, just return input {@link Runnable} when it's {@link TtlRunnable}, * otherwise throw {@link IllegalStateException}. * Caution: {@code true} will cover up bugs! DO NOT set, only when you know why. * @return wrapped tasks * @throws IllegalStateException when input is {@link TtlRunnable} already and not idempotent. */ public static List gets(Collection tasks, boolean releaseTtlValueReferenceAfterRun, boolean idempotent) { if (null == tasks) { return Collections.emptyList(); } List copy = new ArrayList(); for (Runnable task : tasks) { copy.add(TtlRunnable.get(task, releaseTtlValueReferenceAfterRun, idempotent)); } return copy; } }