假设有一个很大的任务。这个任务可以分成 a 、 b 、 c三个步骤同时进行,
在同时进行的情况下,如果有一个任务被取消掉了或者遇到错误了,然后所有的都应该结束,要求高效完成,主要是高效。
严格来讲这是一个分布式事务的问题,也就是说有一个事务,此事务的某一段放到a级上去执行,其余两段分别放到b和c,其中有一个半截单子的子事务没有完成,那整个事务取消掉。
面试题:
关于线程同步的面试题,凡是从时间角度或者是优先级角度考虑解决思路的,基本全不对!凡是从join sleep考虑的,99.99%的不对,线程优雅的结束,一般不用interrupt stop resume,不得已再使用interrupt。
经典面试题:synchronized和ReentrantLock的区别是什么?
答案:ReentrantLock可以多个队列,synchronized只有一个队列,ReentrantLock可以做公平锁,synchronized只有非公平(没有公平可言),ReentrantLock还可以tryLock尝试上锁,上不了锁可以去做别的(做出一定的处理),但是synchronized只能上来就死等傻傻等待,等不到就死在那,第四个区别:锁可以被打断,就是我在锁的过程中我可以让别人打断我,打折我的腿我就醒了这个叫lock.incorruptibly可以做这个操作就是entry.lock,但是synchronized不可以,因为synchronized不能打断,除非把整个线程给砍掉。
面试题:【面试过程中现场实现】
提示:以下是本篇文章正文内容,下面案例可供参考
举例:张三李四一行人现在要吃火锅要有很多步骤
假设现在张三去起锅烧水,李四去买菜了,路人王要去请王二来一起吃。
然后其中路人王在去请王二的路上遇到了pk无法去请王二来吃火锅,这个时候应该考虑什么,整个任务全取消,即便是张三在烧水,李四在买菜也取消,停掉。
示例方案一(存在问题):
可以翻看下面的几个案例有一个循序渐进的过程。
package com.aaa.blbl_Test2;
import java.io.IOException;
public class T0_F1 {
public static void main(String[] args) {
/*假设我们有三个任务,t1、t2、t3,然后假设每一个任务正常执行完结束之后,分别应该是3秒钟、
1秒钟、五秒钟,当然为了模拟这个任务t1、t2的两个任务都能正常执行完,所以后面传true的意思是说
让它正常执行完,穿false的意思是说,让它执行到一秒的时候报个错
因为是模拟任务,所以在这里直接给了线程的执行时间,1000毫秒=1秒*/
Thread t1 = new MyTask("t1",3000,true);
Thread t2 = new MyTask("t2",1000,false);
Thread t3 = new MyTask("t3",5000,true);
//演示回滚过程
t1.start();
t2.start();
t3.start();
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
private static class MyTask extends Thread
{
private String name;//名字
private int timeInSeconds;//执行时间
private boolean success;//执行成功还是失败
public MyTask(String name, int timeInSeconds, boolean success) {
this.name = name;
this.timeInSeconds = timeInSeconds;
this.success = success;
}
@Override
public void run()
{
//模拟业务执行时间
//实际中时间不固定,可能在处理计算任务,或者是等待Io任务
try {
sleep(timeInSeconds);
System.out.println(name+"任务结束!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
存在问题:因为程序正常的结束取决于你最长的这个时间段,比如t1执行时间是十秒sleep(10000)我们整个程序要想结束,别的线程就要等它十秒才能结束。
但是按照题目的要求一秒就应该结束,因为一秒钟有一个任务已经报错了,所以一秒就应该结束。
示例方案二:(小白玩法)
我们给每一个任务,给它指定一个执行的结果,注意看第二个版本的玩法,这里是分成一个一个版本来实现的,如果不分版本的话对于基础稍弱的很可能写不到最后的那个版本。
package com.aaa.blbl_Test2;
import java.util.ArrayList;
import java.util.List;
public class T0_F2 {
public static void main(String[] args) {
/*版本二玩法:
需要有某一个东西来告知这个线程是正常成功了
还是没成功他的状态还是说没结束呢?
所以在下面做了一个枚举类型,这个枚举类型主要,指带的每一个线程它的状态,
一共有三个状态。
第一个状态是NOTEND也就是说现在正在运行之中。
第二个状态:SUCCESSED代表正常运行结束。
第三个状态:FAILED代表运行失败。
这样做的需求,因为需要知道这个线程他执行失败了,需要知道他的状态。
如果执行失败了通知所有的线程结束。
实现步骤:
private static enum Result{
NOTEND,SUCCESSED,FAILED
}
*/
Thread t1 = new T0_F2.MyTask("t1",3000,true);
Thread t2 = new T0_F2.MyTask("t2",1000,false);
Thread t3 = new T0_F2.MyTask("t3",5000,true);
List<MyTask> tasks = new ArrayList<>();
tasks.add((MyTask) t1);
tasks.add((MyTask) t2);
tasks.add((MyTask) t3);
//启动线程
tasks.stream().forEach((t -> t.start()));
/*
接上面:实现步骤:首先把所有的线程全装到一个list里,然后让所有的线程启动,
启动完成之后,就启动一个监视程序也就是下面的for循环,一直不停的检测它。
*/
//启动监视
for (; ; ) {
for (MyTask task : tasks)
{
// 如果有某一个线程getResult()它的Result是FAILED的了整个线程失败、System.exit(0)整个程序结束。
if (task.getResult() == Result.FAILED)
{
System.exit(0);
}
}
}
}
private static enum Result{
NOTEND,SUCCESSED,FAILED
}
private static class MyTask extends Thread
{
private Result result = Result.NOTEND;
private String name;
private int timeInSeconds;
private boolean success;
public MyTask(String name, int timeInSeconds, boolean success) {
this.name = name;
this.timeInSeconds = timeInSeconds;
this.success = success;
}
public Result getResult() {
return result;
}
@Override
public void run()
{
//模拟业务执行时间
//实际中时间不固定,可能在处理计算任务,或者是等待Io任务
try {
sleep(timeInSeconds);
System.out.println(name+"任务结束!");
result = success ? Result.SUCCESSED : Result.FAILED;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
这个方法也是绝对不能够被允许的:

接上图它里面的for循环是一个盲等待,盲等待(就是一直不停循环代表不停的消耗cpu,
是效率上的降低,不能够被允许,第二点:System.exit(0):粗暴的干掉一切线程,
假设路人王去请王二来吃火锅,一秒钟之后失败了,李四在买菜的路上遇到了修路,张三在烧水把锅踢飞,完全有可能是不一致的状态,打开了半截文件直接就退出了,这个文件还没有close,建立了一个链接对方还在等着我说话,这边还没说直接就close了,所以该方案也是存在问题的不能被允许)
示例方案三:(循序渐进)
示例:每一个线程在运行的时候,加一个最终的处理方法叫boss.end
package com.aaa.blbl_Test2;
/**
* 最原始的方法 Thread run()重写
*/
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class T0_F3 {
private static class Boss extends Thread{
private List<Worker> tasks = new ArrayList<>();
public void addTask(Worker t)
{
tasks.add(t);
}
@Override
public void run()
{
tasks.stream().forEach((t) -> t.start());
}
public void end(Worker worker)
{
if (worker.getResult()==Result.FAILED)
{
System.exit(0);
}
}
}
public static void main(String[] args) {
/*
Boss boss = new Boss();
boos是最大管理员,每一个Worker都拥有一个boos的引用,
如果某一个线程结束了,在boss.end(this)处理方法如果该线程Result.FAILED
那么就整个退出 System.exit(0),这样解决了盲等待的问题。
*/
Boss boss = new Boss();
Worker t1 = new Worker(boss,"t1",3000,true);
Worker t2 = new Worker(boss,"t2",1000,false);
Worker t3 = new Worker(boss,"t3",5000,true);
boss.addTask(t1);
boss.addTask(t2);
boss.addTask(t3);
//启动线程
boss.start();
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
private static enum Result{
NOTEND,SUCCESSED,FAILED
}
private static class Worker extends Thread
{
private Result result = Result.NOTEND;
private Boss boss;
private String name;
private int timeInSeconds;
private boolean success;
public Worker(Boss boss, String name, int timeInSeconds, boolean success) {
this.boss = boss;
this.name = name;
this.timeInSeconds = timeInSeconds;
this.success = success;
}
public Result getResult() {
return result;
}
@Override
public void run()
{
//模拟业务执行时间
//实际中时间不固定,可能在处理计算任务,或者是Io任务
try {
sleep(timeInSeconds);
System.out.println(name+"任务结束!");
result = success ? Result.SUCCESSED : Result.FAILED;
boss.end(this);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
最终方案:
书写回滚,回滚这个事件就是每种是每种不同的方式,就是每一个线程有每一个线程不同的回滚方式。
package com.aaa.blbl_Test2;
import java.util.ArrayList;
import java.util.List;
/**
* 最原始的方法 Thread run()重写
*/
public class F0_F4 {
private static class Boss extends Thread{
private List<Worker> tasks = new ArrayList<>();
public void addTask(Worker t)
{
tasks.add(t);
}
@Override
public void run()
{
tasks.stream().forEach((t) -> t.start());
}
public void end(Worker worker)
{
//当某一个线程结束了,并不是把其他线程的腿打断
//而是要让它取消cancel
if (worker.getResult() == Result.FAILED)
{
cancel(worker);
}
}
private void cancel(Worker worker)
{
for (Worker w : tasks)
{
if (w != worker) w.cancel();
}
}
}
public static void main(String[] args) {
Boss boss = new Boss();
Worker t1 = new Worker(boss,"A===",3,true);
Worker t2 = new Worker(boss,"B===",1,false);
Worker t3 = new Worker(boss,"C===",5,true);
boss.addTask(t1);
boss.addTask(t2);
boss.addTask(t3);
//启动线程
boss.start();
try {
System.in.read();
} catch (Exception e) {
e.printStackTrace();
}
}
private static enum Result{
NOTSET,SUCCESSED,FAILED,CANCELLED
}
private static class Worker extends Thread
{
private Result result = Result.NOTSET;
private Boss boss;
private String name;
private int timeInSeconds;
private boolean success;
private volatile boolean cancelling = false;
public Worker(Boss boss, String name, int timeInSeconds, boolean success) {
this.boss = boss;
this.name = name;
this.timeInSeconds = timeInSeconds;
this.success = success;
}
public Result getResult() {
return result;
}
@Override
public void run()
{
int interval = 100;
int total = 0;
for ( ; ; )
{
try {
sleep(interval);//cpu密集型
total += interval;
if (total / 1000 >= timeInSeconds)
{
// System.out.println(total+"$$$$");
// System.out.println(timeInSeconds);
System.out.println(name+"任务结束!"+result);//正常结束
result = success ? Result.SUCCESSED : Result.FAILED;
System.out.println("的撒大");
break;
}
if (cancelling)
{
rollback();
result = Result.CANCELLED;
cancelling = false;
System.out.println(name="执行完毕!"+result);
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
boss.end(this);
}
private void rollback()
{
try {
//书写回滚
System.out.println(name+"rollback start....");
/* rollback事务回滚,不同的事务是不同的回滚
举例:所有事件整体是一个大事务,它要求第一个事务是关于数据库的
那么插入五条数据就要(cancel)删除五条数据、假设第二个事务是关于文件拷贝
文件拷贝完之后就要进行删除、第三个是关于MQ里面往里面发消息的
假设发进去几条消息,然后在发几条取消掉的消息这叫rollback
如果在rollback过程中出错,假设插入五条数据删到三条的时候出错了
举例:发出总任务的这个人一定有一个最大等待时间,
假如你在特定的时间内没有完成,那就说明任务失败,那其中有一个子任务,
那这个子任务,如果中间rollback失败了,那就是一定抛出异常了
这个时候一定要记录日志,发告警必须人工处理了,那上面的数据的那个来说删不掉了
说明里面有脏数据了,可能不是删除可能是修改,这时候必须人工处理了,把运维程序员从睡梦中叫醒来处理
*/
sleep(500);
System.out.println(name+"rollback end!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void cancel() {
cancelling = true;//设立标志位
}
}
}
先进处理方案:
package com.aaa.blbl_Test2;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* 问题:可以归分为分布式事务失败回滚的手工实现
* 代码不完善,可扩展
*/
public class F0_F5 {
//任务执行结束的三种状态
private static enum Result
{
SUCCESS,FAIL,CANCELLED
}
static List<MyTask> tasks = new ArrayList<>();
public static void main(String[] args) {
MyTask task1 = new MyTask("task1===",3,Result.SUCCESS);
MyTask task2 = new MyTask("task2===",5,Result.SUCCESS);
MyTask task3 = new MyTask("task3===",1,Result.FAIL);
tasks.add(task1);
tasks.add(task2);
tasks.add(task3);
for (MyTask task : tasks)
{
/*可以使用线程的CompletableFuture可以使用异步任务这个线程池CompletableFuture.supplyAsync
使用纯异步的方式去调用第一个任务,第一个任务如果有了结果之后,
用callback处理结果。
*/
CompletableFuture f = CompletableFuture.supplyAsync(() -> task.runTask())
.thenAccept((result -> callback(result,task)));
}
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static Result callback(Result result,MyTask task)
{
//在处理结果的时候,判断结果如果是FAIL那么让所有的任务全部cancel
if (Result.FAIL == result)
{
for (MyTask ts : tasks )
{
if (ts!=task)
{
ts.cancel();
}
}
}
return result;
}
private static class MyTask
{
private String name;
private int timeInSeconds;
private Result ret;
boolean cancelling = false;
volatile boolean cancelled = false;
public MyTask(String name, int timeInSeconds, Result ret) {
this.name = name;
this.timeInSeconds = timeInSeconds * 1000 ;
this.ret = ret;
}
public Result runTask()
{
int interval = 100;
int total = 0;
try {
for (; ;)
{
Thread.sleep(interval);
if (cancelling) continue;
total += interval;
if (total >= timeInSeconds)break;
if (cancelled) return Result.CANCELLED;
}
}catch (Exception e)
{
e.printStackTrace();
}
System.out.println(name+"end!");
return ret;
}
//任务cancel过程synchronized (this)
public void cancel()
{
cancelling = true;
//不上锁的话关系也不大
synchronized (this)
{
System.out.println(name+"cancelling");
try {
//然后让它去任务结束
Thread.sleep(50);
System.out.println(name+"cancelled");
} catch (InterruptedException e) {
e.printStackTrace();
}
//最后cancel
cancelled = true;
}
}
}
}
以上就是今天要讲的内容,以上代码还有可扩展性,当然“笔者还深度欠缺,如果错误还请指正”。
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po
尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search
我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的
由于fast-stemmer的问题,我很难安装我想要的任何rubygem。我把我得到的错误放在下面。Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingfast-stemmer:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcreatingMakefilemake"DESTDIR="cleanmake"DESTDIR=
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www