Java多线程stop

暴力的stop

import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

public class StopTest {
    
    static Object lock = new Object();
    
    @SuppressWarnings("deprecation")
    public static void main(String[] args) {
        User u1 = new User();
        u1.setId(1);
        u1.setAmount(new BigDecimal(1000));
        User u2 = new User();
        u2.setId(2);
        u2.setAmount(new BigDecimal(0));
        System.out.println(u1);
        System.out.println(u2);
        BigDecimal changeAmount = new BigDecimal(500);
        Change change = new Change(u1,u2,changeAmount);
        change.start();
        try {
            Thread.sleep(1000);//让change线程执行1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        change.stop();
         try {
            change.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(u1);
        System.out.println(u2);
    }
    
    static class Change extends Thread{
        private User u1,u2;
        private BigDecimal change;
        
        Change(User u1,User u2,BigDecimal change){
            this.u1 = u1;
            this.u2 = u2;
            this.change = change;
        }
        @Override
        public void run() {
            synchronized(lock){
                BigDecimal subResult = u1.amount.subtract(change);
                u1.setAmount(subResult);
                try {
                    TimeUnit.SECONDS.sleep(2);//回main线程,让main线程stop当前线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                BigDecimal addResult = u2.amount.add(change);
                u2.setAmount(addResult);
            }
        }
    }
    
    static class User{
        private int id;
        
        private BigDecimal amount;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public BigDecimal getAmount() {
            return amount;
        }

        public void setAmount(BigDecimal amount) {
            this.amount = amount;
        }

        @Override
        public String toString() {
            return "User [id=" + id + ", amount=" + amount + "]";
        }
        
    }

}

这个实例是想把用户u1的amount扣除500,用户u2的amount加上500的,这种类型的事务看似简单,但是在实际应用中却非常多。在上面的例子中我们有意让同步快只执行一半,就在main中stop了。这样就很容易发现用户u1扣除了500,但是用户u2并没有加上500,显然是不满足约束的,数据的一致性遭到了破坏。

可能有同学会问:实际中谁会写这么傻的代码啊?的确不会这么极端,但是使用了stop基本本质上就和这么傻的代码差不多了。除非你非常清楚的知道自己的run逻辑代码中在任意位置终止都不存在破坏数据约束的行为。

stop的代替模式

上面我们说了stop的不安全的,但是我们有时候的确需要在其他线程中终止一个线程怎么办呢?我们把上面的代码简单的改一下就可以了,可能例子并不少非常恰当,但是模式是通用的。

import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

public class StopTest2 {
    
    private volatile static boolean stoped = false;
    
    static Object lock = new Object();
    
    public static void stop(){
        stoped = true;
    }
    
    public static void main(String[] args) {
        User u1 = new User();
        u1.setId(1);
        u1.setAmount(new BigDecimal(1000));
        User u2 = new User();
        u2.setId(2);
        u2.setAmount(new BigDecimal(0));
        System.out.println(u1);
        System.out.println(u2);
        BigDecimal changeAmount = new BigDecimal(500);
        Change change = new Change(u1,u2,changeAmount);
        change.start();
        try {
            Thread.sleep(1000);//让change线程执行1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//        change.stop();
        StopTest2.stop();
        try {
            change.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(u1);
        System.out.println(u2);
    }
    
    static class Change extends Thread{
        private User u1,u2;
        private BigDecimal change;
        
        Change(User u1,User u2,BigDecimal change){
            this.u1 = u1;
            this.u2 = u2;
            this.change = change;
        }
        @Override
        public void run() {
            while(!stoped){
            synchronized(lock){
                BigDecimal subResult = u1.amount.subtract(change);
                u1.setAmount(subResult);
                try {
                    TimeUnit.SECONDS.sleep(2);//回main线程,让main线程stop当前线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                BigDecimal addResult = u2.amount.add(change);
                u2.setAmount(addResult);
            }
            }
        }
    }
    
    static class User{
        private int id;
        
        private BigDecimal amount;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public BigDecimal getAmount() {
            return amount;
        }

        public void setAmount(BigDecimal amount) {
            this.amount = amount;
        }

        @Override
        public String toString() {
            return "User [id=" + id + ", amount=" + amount + "]";
        }
        
    }

}

首先有一个volatile的Boolean变量stoped来标记线程释放终止,volatile关键字是为了保证多线程的可见性,如果有线程修改了,其他线程立即可见。然后提供一个共有接口来修改stoped的值。在业务逻辑代码run中根据业务逻辑需要处理线程终止,一般需要终止的线程是一个无限循环,这里只是为了说明终止线程的模式硬编上去的。

stop的代替方法

其实还可以利用Java提供给我们的接口来终止线程,Java提供了3个相关的接口:、

//Object的 wait(), wait(long),wait(long, int) 
//或者Thread的join(), join(long), join(long, int), 
//sleep(long), sleep(long, int) 会清除线程的中断标记,
//其他情况会,添加中断标志
public void interrupt()
public boolean isInterrupted()//检查是否中断
public static boolean interrupted()//静态方法,判断是否中断,清除中断状态
import java.math.BigDecimal;

public class StopTest4 {
    
    static Object lock = new Object();
    
    public static void main(String[] args) {
        User u1 = new User();
        u1.setId(1);
        u1.setAmount(new BigDecimal(1000));
        User u2 = new User();
        u2.setId(2);
        u2.setAmount(new BigDecimal(0));
        System.out.println(u1);
        System.out.println(u2);
        BigDecimal changeAmount = new BigDecimal(500);
        Change change = new Change(u1,u2,changeAmount);
        change.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//        change.stop();
        change.interrupt();
        try {
            change.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(u1);
        System.out.println(u2);
    }
    
    static class Change extends Thread{
        private User u1,u2;
        private BigDecimal change;
        
        Change(User u1,User u2,BigDecimal change){
            this.u1 = u1;
            this.u2 = u2;
            this.change = change;
        }
        @Override
        public void run() {
           
            synchronized(lock){
                BigDecimal subResult = u1.amount.subtract(change);
                u1.setAmount(subResult);
                while(!Thread.currentThread().isInterrupted());//空循环
                BigDecimal addResult = u2.amount.add(change);
                u2.setAmount(addResult);
            }
            
        }
    }
    
    static class User{
        private int id;
        
        private BigDecimal amount;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public BigDecimal getAmount() {
            return amount;
        }

        public void setAmount(BigDecimal amount) {
            this.amount = amount;
        }

        @Override
        public String toString() {
            return "User [id=" + id + ", amount=" + amount + "]";
        }
        
    }

}

这个例子也是硬编上去的,只是为了说明用interrupt来终止线程,interrupt并不会想stop那样简单粗暴的终止线程,有点像stop模式,但是对于使用interrupt来说,检查标志位的方式不统一,这在处理上会有一定的麻烦,所以很多时候使用stop模式。

总结

  1. 尽量不要使用stop的方法,考虑使用wait,notify模式
  2. 如果的确要使用stop的方法,可以考虑interrupt方法,建议考虑使用stop模式

转载于:https://my.oschina.net/u/2474629/blog/891837

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值