【java异步核心原理】1.动手实现Netty ChannelFuture机制 2.ExecutorService实现多线程业务计算并把结果包装为Task提交到另外一个单线程池执行从而拿到结果

Callback.java

package async;

@FunctionalInterface
public interface Callback<T> {
    void call(T t);
}

 ChannelFuture.java

package org.example.async;

import java.util.concurrent.ExecutorService;

public interface ChannelFuture<IN, OUT> {
	FutureTask addListener(int hashId, Task<IN, OUT> task, IN input, Callback<OUT> callback);

	static <T, R> ChannelFuture<T, R> newPromise(ExecutorService es) {
		return new ChannelFutureImpl<>(es);
	}
}

ChannelFutureImpl.java

package org.example.async;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ChannelFutureImpl<IN, OUT> implements ChannelFuture<IN, OUT> {

	private ExecutorService es;
	private final static ExecutorService[] _esArray = new ExecutorService[Runtime.getRuntime().availableProcessors()];

	static {
		for (int i = 0; i < _esArray.length; i++) {
			final String threadName = "Processor_" + i;
			_esArray[i] = Executors.newSingleThreadExecutor((newRunnable) -> {
				Thread newThread = new Thread(newRunnable);
				newThread.setName(threadName);
				return newThread;
			});
		}
	}

	public ChannelFutureImpl(ExecutorService es) {
		this.es = es;
	}

	@Override
	public FutureTask addListener(int hashId, Task<IN, OUT> task, IN input, Callback<OUT> callback) {
		final FutureTask<OUT> future = new FutureTask<>();
		if (hashId < 0) {
			hashId = 0;
		}
		if (hashId >= _esArray.length) {
			hashId = _esArray.length - 1;
		}

		_esArray[hashId].execute(() -> {
			// 阻塞的在别的线层等待执行完毕
			OUT result = task.get(input);
			future.setSuccess(result);

			es.execute(() -> {
				// 将结果回调过去
				if (null != callback) {
					callback.call(result);
				}
			});
		});

		return future;
	}
}

Future.java

package org.example.async;


public interface Future<T>
{
	T sync() throws InterruptedException;
}

FutureTask.java

package org.example.async;

public class FutureTask<T> implements Future<T> {
	private T result;
	private boolean isDone = false;
	private final Object LOCK = new Object();

	@Override
	public T sync() throws InterruptedException {
		synchronized (LOCK) {
			while (!isDone) {
				LOCK.wait();
			}

			return result;
		}
	}

	protected void setSuccess(T result) {
		synchronized (LOCK) {
			if (isDone){
				return;
			}

			this.result = result;
			this.isDone = true;
			LOCK.notifyAll();
		}
	}
}

 Task.java

package async;

public interface Task<IN, OUT>
{
    OUT get(IN input);
}

测试

package org.example.async;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class FutureTest {
	public static void main(String[] args) throws InterruptedException {
		// 业务线程
		ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {
			@Override
			public Thread newThread(Runnable r) {
				Thread t = new Thread(r);
				t.setName("业务线程");
				return t;
			}
		});

		for (int i = 0; i < 10; i++) {
            // es作为参数,是为了当异步计算完毕,将回调结果传递到es所在的业务线程
			ChannelFuture<String, String> future = ChannelFuture.newPromise(es);
			FutureTask promise = future.addListener(i % 5, input -> {
				try {
					TimeUnit.SECONDS.sleep(3);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				return input + " World";

			}, "Hello" + i, (ret) -> {
				System.out.println(ret + ":" + Thread.currentThread().getName());
			});

			// 加上这行后,就会同步的等待结果
//			System.out.println(promise.sync());
		}
	}
}

/*
Hello3 World:业务线程
Hello0 World:业务线程
Hello2 World:业务线程
Hello1 World:业务线程
Hello5 World:业务线程
Hello6 World:业务线程
Hello7 World:业务线程
Hello4 World:业务线程
Hello8 World:业务线程
Hello9 World:业务线程
 */

总结:

不管是异步还是什么,线程间的同步使用synchronized+wait+notify+notifyAll机制用的不少。

直接new接口,并实现方法就可以搞出来一个匿名类。

异步为了不阻塞调用者线程必然是在别的线程计算,计算完后要回到调用线程。

之所以调用get后阻塞就是因为: 在拿到结果前,wait了,别的线程计算完毕赋值好,通过lock.notify即可拿到结果,这就是线程之间通信的机制。

泛型很好用,这样子写一遍代码,就可以用到所有的数据类型了。

-------------------------------------------20210917------------------------------------------

Worker多线程进行任务计算,并把结果提交到逻辑单线程池,从而在逻辑线程拿到Worker的结果,实现并发安全

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,可以使用Netty框架来实现后台的实时传送到台。Netty是高性能的网络编框架,提供异步事件驱动的应用程序开发模型。 以下是简的示例代码,展示了何使用Netty实现后台数据的时传送到前台: ```java import io.netty.bootstrap.ServerBootstrap; import io.channel.ChannelFuture; import.netty.channel.ChannelInitializer; import.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.stream.ChunkedWriteHandler; public class Server { private int port; public Server(int port) { this.port = port; } public void run() throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( new StringDecoder(), new StringEncoder(), new ChunkedWriteHandler(), new ServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { int port = 8080; new Server(port).run(); } } ``` 在这个示例中,我们创建了一个Netty服务器,并且指定了端口号为8080。在`ChannelInitializer`的`initChannel`方法中,我们添加了一系列的处理器,包括`StringDecoder`和`StringEncoder`用于处理字符串消息的编解码,`ChunkedWriteHandler`用于处理大数据流的传输,以及自定义的`ServerHandler`用于处理具体的业务逻辑。 你可以根据自己的需求,在`ServerHandler`中实现数据传送的逻辑。例如,可以在收到新数据时将数据发送到前台。 需要注意的是,这只是一个的示例代码,实际使用时可能需要根据具体的业务需求进行适当的修改和扩展。 希望这个示例能对你有所帮助!如果你有任何其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值