Java线程(一)

来源:互联网 发布:php开发流程 编辑:程序博客网 时间:2024/06/05 07:11

新建线程

继承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并不会立刻执行打印抛出异常,一定需要重新获得锁