Java线程(一)

新建线程

继承Thread
实现Runnable

停止线程

stop方法是一个被废弃的方法,因为其是暴力的,即不管线程执行的状态如何,都会立刻停止线程,可能会引起数据不一致的情况,数据不一致的情况见如下代码:

public class UnformmatedStop {
    static class Person{
        private String username;
        private String password;
        public String getUsename() {
            return username;
        }
        public void setUsename(String usename) {
            this.username = usename;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }

        public synchronized void  change(String username,String password){
            this.username = username;

            try {
                //TODO other things
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            this.password = password;
        }

        public synchronized String read(){
            return username + ","+password;
        }
    }

    static class Task implements Runnable{
        private volatile Person p;

        public Task(Person p) {
            super();
            this.p = p;
        }

        @Override
        public void run() {
            System.out.println(p.read());
        }
    }

    static class Task2 implements Runnable{
        private volatile Person p;

        public Task2(Person p) {
            super();
            this.p = p;
        }

        @Override
        public void run() {
            p.change("lq", "084012151");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Person p = new Person();

        Thread tChange = new Thread(new Task2(p));
        tChange.start();
        Thread.sleep(3000);
        tChange.stop();
        Thread tRead = new Thread(new Task(p));
        tRead.start();
    }
}

结果如下:

lq,null

结果分析:tChange线程在写入username和password的时候,被强迫停止了,造成password没有被写成功,tRead读取的时候就会造成password为null了,这就造成了丢失更新的出现

正确的停止一个线程的方法

public class Stop {
    static class Task implements Runnable{
        public volatile boolean stooped = false;

        @Override
        public void run() {
            while(!stooped){
                System.out.println(Thread.currentThread().getName()+"正在运行");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Task task = new Task();
        Thread t1 = new Thread(task);
        t1.start();
        Thread.sleep(3000);
        System.out.println("下面开始暂停线程t1的执行");
        task.stooped = true;
    }
}

注意:volatile的使用,volatile会保证可见性,但是不保证原子性(load,use,rewrite)这个整个过程的原子性

线程中断

线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程, 有人希望你退出啦!至于目标线程接到通知后如何处理,则完全由目标线程自行决定。这点很 重要,如果中断后,线程立即无条件退出,我们就又会遇到stop()方法的老问题。

/**
 * 线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程有人希望你退出了
 * 至于目标线程接到通知后何时如何处理则由目标线程自行决定
 */
public class Interrupt implements Runnable{

    @Override
    public void run() {
        while(true){
            //中断的两个方法,interupt和isInterrupted(判断当前线程是否被中断)两个方法
            /*if(Thread.currentThread().isInterrupted()){
                System.out.println(Thread.currentThread().getName()+"已经被中断");
                break;
            }*/

            Thread.yield();
        }
    }


    public static void main(String[] args) {
        Thread thread = new Thread(new Interrupt());

        thread.start();

        //在这里因为Runnable实现类中并没有对中断处理的逻辑,因此即使thread线程被设置了中断状态
        //但是这个中断并不会发生任何作用,所以需要放开上面的注释部分,对线程的中断逻辑进行处理
        thread.interrupt();
    }
}

当sleep遇上interrupt

public static native void sleep(long millis) throws InterruptedException

sleep方法会抛出InterruptedException异常
InterruptedException不是运行时异常,也就是说程序必须捕获并且处理它,当线程在sleep() 休眠时,如果被中断,这个异常就会产生

public class Sleep {
    static class Task implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"正在运行");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"结束运行");
        }
    }

    public static void main(String[] args) {
        Task task = new Task();
        Thread t1 = new Thread(task);
        t1.start();
        //阻断正在睡眠的线程,会使线程抛出中断异常,然后线程会退出休眠的状态,执行线程后面的代码
        t1.interrupt();
    }
}
Thread-0正在运行
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at sync.Sleep$Task.run(Sleep.java:11)
    at java.lang.Thread.run(Unknown Source)
Thread-0结束运行

线程的等待(wait)和通知(notify)

这里写图片描述
这里还需要强调一点,Objectwait()方法并不是可以随便调用的。它必须包含在对应的 synchronzied语句中,无论是wait()或者notify()都需要首先获得目标对象的一个监视器。

/**
 * 假设线程A调用了obj.wait方法,那么线程A就会被停止执行,转为等待状态(并且会释放锁),等待到何时结束呢?线程A会一直
 * 等待到其他线程调用了obj.notify方法为止(调用obj.notify并不会释放锁),但是线程A在被唤醒后要做的第一件事并不是执行后续的代码,而是要
 * 重新获取obj这个监视器,如果暂时获取不到就会等待这个监视器,当监视器到来后才会真正意义上的唤醒
 * 
 * 就像这里的线程2并不会在线程1,notify后就立刻执行后面的代码一样,这样就会存在延时
 */
public class WaitNotify {

    public static void main(String[] args) {
        final ListAdd listAdd = new ListAdd();

        final Object lock = new Object();

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    for(int i = 0;i<10;i++){
                        listAdd.add();
                        System.out.println(Thread.currentThread().getName()+"添加了一个元素");
                        if(listAdd.size()==5){
                            //notify必须与sychronized一起使用,notify表示不释放锁,wait表示释放锁
                            lock.notify();
                        }
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            }
        });


        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock) {
                    if(listAdd.size()!=5){
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(Thread.currentThread().getName()+"遇到了size为5的情况");
                    throw new RuntimeException("size为5");
                }
            }
        });

        /**
         * 必须先启动线程2,因为只有这样,会先进入线程2的wait方法进行等待,并且释放锁,然后执行线程1
         * 如果先启动线程1的话,线程1在size等于5的时候,并不会释放锁,而是继续往list里面添加数据直到全部完成释放锁,
         * 然后线程2执行的时候size已经是10了,不存在为5的情况了,实验结果就不对了
         */
        thread2.start();
        thread1.start();
    }
}

class ListAdd{
    private volatile List list = new ArrayList();

    public void add(){
        list.add("jyw");
    }

    public int size(){
        return list.size();
    }
}
运行结果
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-0添加了一个元素
Thread-1遇到了size为5的情况
Exception in thread "Thread-1" java.lang.RuntimeException: size为5
    at sync.WaitNotify$2.run(WaitNotify.java:56)
    at java.lang.Thread.run(Unknown Source)

结果分析:刚开始的时候,listAdd的size不是5,所以就会wait,使得当前线程被挂起,等待notify,然后当i=5的时候,thread1会notify,但是此时thread1并没有释放lock对象监视器,所以thread2并不会立刻执行打印抛出异常,一定需要重新获得锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值