ThreadPool -逐渐更新

1. SpringBoot中如何用自己的ThreadPool

ThreadPoolExecutor

@Component
public class MyThread implements Callable<Response> {
	private String param;
	public MyThread (String param) {
		this.param = param;
	}
	@Override
	public Response call() {
		return yourBean.method1(param);
	}
}

// how to initialize it in another class:
public MyClass{
	@Autowired
	private ApplicationContext applicationContext;
	private String param = "abc";
	
	MyThread myThread = applicationContext.getAutowireCapableBeanFactory.getBean(MyThread.class, param);
	
	executor.submit(myThread);  
}

2. submit vs invokeAll

List<Future<String>> futures = new ArrayList<>();
for(Callable callableItem: myCallableList){
    futures.add(executorService.submit(callableItem));
}
// vs:
List<Future<String>> futures = executorService.invokeAll(myCallableList));
  • Submit: You are submitting the tasks to ExecutorService and you are not waiting for the completion of all tasks, which have been submitted to ExecutorService

  • invokeAll: You are waiting for completion of all tasks, which have been submitted to ExecutorService.

3. Does a Future timeout kill the Thread execution

It does not. Why would it? Unless you tell it to, e.g., using Future.cancel, but it’s still not necessarily stop the thread, 见第4点

Future<?> future = service.submit(new MyCallable());
    try {
        future.get(100, TimeUnit.MILLISECONDS);
    } catch (Exception e){
        e.printStackTrace();
        future.cancel(true); //this method will stop the running underlying task
    }

4. Future.cancel并不保证每次都成功stop thread

https://stackoverflow.com/questions/28043225/future-cancel-does-not-work
But that won't necessarily stop the thread.... In this case, the interrupt method just sets a flag called the "interrupt status", which tells the thread to stop when it can.

加深理解,可以用以下代码试一下,我是写在JUnit里的:

public class TestException{
	
	@Test
	public void test(){
		TestThread thread = new TestThread();
		Future<Response> result = executor.submit(thread);
		Response response;
		StringBuilder error;
		try {
			response = result.get(10, TimeUnit.MILLISECONDS);  // 10 < 100
		} catch(TimeoutException e) {
			error.append(e);  // this will always happens
			response = Response.status(Response.Status.INTERNAL_SERVER_ERROR.entity(e).build();
			result.cancel(true);  // but this may not succeeded
		} catch (Exception e) {
			error.append(e); 
			response = Response.status(Response.Status.INTERNAL_SERVER_ERROR.entity(error).build();
		}
		
		assertThat(response.getEntity().toString(), containsString("TimeoutException"));  // always succeeded 
		
	}
}
class TestThread implements Callable<Response> {
	TestThread(){}
	@Override
	public Response call() {
		try{
			System.out.println("thread start");
			Thread.sleep(100); // sometimes will throw sleep interrupted exception
			System.out.println("thread end");  // sometimes will reach this
			return Response.status(Response.Status.OK).entity("Thread 1 Response").build();
		} catch (Exception e) {
			System.out.println("sleep interrupted error: " + e); // doesn't always happens
			return Response.status(Response.Status.INTERNAL_SERVER_ERROR.entity(e.getMessage()).build();
		}
	
	}
}

我的代码大概是这样:

@path('cance-thread/{uuid}')
public String cancel(@PathParam('uuid') String uuid) {
	Future<Response> thread = threadsMap.get(uuid);
	thread.cancel();
	executor.purge();
}

你会发现,call这个cancel api,不是每次都会print “thread end”,也不是每次Thread.sleep()都能被成功interrupted. 具体的可以见代码里的注释。

所以很多人就采用循环去不断检测,比如5里有提到,但我并没有使用这些,所以5只能算个零散的学习整理。

5. How to stop a Callable submitted to ExecutorService?

https://stackoverflow.com/questions/17840238/i-want-my-thread-to-handle-interruption-but-i-cant-catch-interruptedexception

cancelling a running Task is not that easy, you could try following: call cancel(true); it will set Thread.interrupted() to true. Now in your Task check on some steps that value and so you can decide to skip next steps of your task

Using the shutdown will bring down the entire executorService… This executor service might also be processing few more threads…

You can cancel a future using Future.cancel() method. It attempts to cancel the execution of the task and returns true if it is cancelled successfully, otherwise, it returns false.

The cancel() method accepts a boolean argument - mayInterruptIfRunning. If you pass the value true for this argument, then the thread that is currently executing the task will be interrupted, otherwise in-progress tasks will be allowed to complete.

但是我并没有用到下面这循环检测是否被interrupt的方法,然后我用future.cancel的也是可以立刻cancel thread的。我是用一个map存着正在run的thread(future),然后需要cancel的时候,拿出这个thread/future,然后用future.cancel(true),这个线程立刻就会被终止并且throw一个interruptException,但是这个exception好像catch不住… 所以可能下面这个循环就是帮你throw exception,这样你就可以有地方catch住这个exception了吧。

for(int i = 0; i < 1000; i++){
    // Uses isInterrupted() to keep interrupted status set
    if (Thread.currentThread().isInterrupted()) {
        // Cannot use InterruptedException since it's checked
        throw new RuntimeException(); 
    }
    System.out.println(i);
}

但我其实不需要…因为我已经是最外层的call了,没地方能catch这里throw的runtimeException…而且后端throw一个exception也不影响spring application的运行。

typical implementations will cancel via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.

这里其实也介绍了如何终止thread并且catch exception,感觉这几种写法比我的直接cancel可能更严谨:

Therefore, when you call ExecutorService.shutdownNow() the implementation will try to shut down all currently executing tasks. each task will keep running until it reaches a point where it detects it was interrupted.

In order to assure your threads are reaching that point at early stage it is a good idea to add a check in your loop whether the thread is interrupted:

private Callable<Object> getTestAllCallable() {
    return new Callable<Object>() {
        @Override
        public Object call() {
            for (int i = 0; i < inbuiltProxyPojoArrayList.size(); i++) {
                if(Thread.currentThread().isInterrupted()) {
                    return null;
                }
                if(someCondition) {
                    //someWork
                } else {
                    MyLogger.log(MyLogger.LOG_TYPE.DEBUG, "ThreadInterrupted-Cancelling");
                    return null;
                }
            }
            return null;
        }
    };
}

6. executor.purge()

然后为了保险起见,在future.cancel(true)后,我又加了executor.purge(),是能够把cancel的thread真正地从queue里remove掉的。

once it will be pulled by the Executor but until then its still in the queue
try to use threadPoolExecutor.purge(); right after cancel(); it will try to remove the canceled tasks.

purge一般用于一次性清理比较多的cancelled的thread,另外不保证purge能成功,有的thread可能被block或者正在执行等,不能够被purge成功。另外,应该是会有自动清除机制的。

7. Thread @Scope, Spring objects - Singleton vs Prototype Scope, thread safety?

看到在Thread上有时候会有@Scope的注解,有些困惑所以学习一下。遇到的情况如下:

@Component
@Scope("prototype")
public class MyThread implements Callable<Response> {
	private String param;
	public MyThread (String param) {
		this.param = param;
	}
	@Override
	public Response call() {
		return yourBean.method1(param);
	}
}

7.1 Singleton vs Prototype - Example 1

主要区别在于,看如下results:
大概是两个线程分别在用一个Object的setter方法对count进行count++,然后用singleton的,这两个线程其实在操作同一个instance:

Singleton Scope在这里插入图片描述
不同thread共用同一个instance。

Prototype Scope
在这里插入图片描述
不同的instance/实例各忙各的,互不干扰。因为instance不是同一个instance。

7.2 Singleton vs Prototype - Example 2: What is difference between singleton and prototype bean?

	...
		// get the bean from the spring container
       Coach theCoach = context.getBean("tennisCoach",Coach.class);
       Coach alphaCoach = context.getBean("tennisCoach",Coach.class);
       // call a method on the bean
       System.out.println("Are the two beans same :" + (theCoach==alphaCoach));
       System.out.println("theCoach : " + theCoach);
       System.out.println("alphaCoach: "+ alphaCoach);
       context.close()

    }
}
// For singleton scope the output is :
Are the two beans same :true
theCoach : com.springdemo.TennisCoach@2a53142
alphaCoach: com.springdemo.TennisCoach@2a53142

// For prototype scope the output is :
Are the two beans same :false
theCoach : com.springdemo.TennisCoach@1b37288
alphaCoach: com.springdemo.TennisCoach@1a57272

7.3 Non qualifying bean of "String"

感觉组里小哥意思是多线程用prototype比较好,但其实我觉得我的情况用哪个都行。因为两个都不能确保是thread safe的,thread safe取决于你的code怎么写,而不取决于你用了那种模式。Are Spring Objects thread safe?

但我当时遇到了个问题,为了快速解决,我还是用了prototype。因为singleton在spring中是一开始就instantiate的, 然后就会抱一个错: Non qualifying bean of "String", 报错类似这个

之后我改成@Scope(‘prototype’)就不报错了,解决方法及原因是:
Non qualifying bean of “String” error solved by:
@Scope(value = “prototype”) means that Spring will not instantiate the bean right on start, but will do it later on demand.

我又傻了
好像写一个no arguments的constructor即可,这样用singleton说不定也可以了,不过没有试过。

7.4 How multiple threads invoke singleton object’s method and work on them?

In a Singleton Object you have:

Fields: They are stored in memory. They can be shared amongst multiple threads and you have no guarantee they will keep consistent (unless you make them synchronized).
Methods to be called: They can be called from more than one thread. Each execution is independent and thread safe, unless they access some shared field improperly.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值