美文网首页
Java——Thread VS Runnable

Java——Thread VS Runnable

作者: Charon1997 | 来源:发表于2017-10-30 11:16 被阅读0次

    一、线程创建的两种方式

    1.1继承Thread

    class MyThread extends Thread{
            @Override
            public void run() {
                
            }
        }
    //创建线程
    MyThread myThread = new MyThread();
    //启动线程
    myThread.start();
    

    1.2实现Runnable接口

    class MyThread implements Runnable{
    
            @Override
            public void run() {
                
            }
        }
    MyThread mt = new MyThread();
    Thread td = new Thread(mt);
    td.start();
    

    1.3比较

    1. Runnable方式可以避免Thread方式由于Java单继承特性带来的缺陷
    2. Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况

    二、卖火车票

    2.1 Thread实现

    public class TicketsThread {
    
        public static void main(String[] args) {
            MyThread myThread1 = new MyThread("一号窗口");
            MyThread myThread2 = new MyThread("二号窗口");
            MyThread myThread3 = new MyThread("三号窗口");
            
            myThread1.start();
            myThread2.start();
            myThread3.start();
        }
    
    }
    
    class MyThread extends Thread{
        private int ticketsCont = 5 ;
        private String name;
        public MyThread(String name) {
            this.name = name;
        }
        
        @Override
        public void run() {
            while (ticketsCont > 0) {
                //有票就卖一张
                ticketsCont--;
                System.out.println(name+"卖了一张票,剩余票数为"+ ticketsCont);
            }
        }
    }
    

    运行结果

    二号窗口卖了一张票,剩余票数为4
    二号窗口卖了一张票,剩余票数为3
    二号窗口卖了一张票,剩余票数为2
    二号窗口卖了一张票,剩余票数为1
    二号窗口卖了一张票,剩余票数为0
    三号窗口卖了一张票,剩余票数为4
    三号窗口卖了一张票,剩余票数为3
    三号窗口卖了一张票,剩余票数为2
    三号窗口卖了一张票,剩余票数为1
    三号窗口卖了一张票,剩余票数为0
    一号窗口卖了一张票,剩余票数为4
    一号窗口卖了一张票,剩余票数为3
    一号窗口卖了一张票,剩余票数为2
    一号窗口卖了一张票,剩余票数为1
    一号窗口卖了一张票,剩余票数为0
    
    

    结果并不能满意。没有对票数这个多线程共同访问的数据进行同步,使得每一个线程都有自己的一个数据源。

    2.2Runnable实现

    public class TicketsRunnable {
    
        public static void main(String[] args) {
            MyThread mThread = new MyThread();
            Thread thread1 = new Thread(mThread,"窗口1");
            Thread thread2 = new Thread(mThread,"窗口2");
            Thread thread3 = new Thread(mThread,"窗口3");
            
            thread1.start();
            thread2.start();
            thread3.start();
        }
    
    }
    
    class MyThread implements Runnable{
        private int ticketsCont = 5;
        
        @Override
        public void run() {
            while (ticketsCont > 0) {
                //有票就卖一张
                ticketsCont--;
                System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余票数为"+ ticketsCont);
            }
        }
    }
    

    运行结果

    窗口1卖了一张票,剩余票数为4
    窗口1卖了一张票,剩余票数为3
    窗口3卖了一张票,剩余票数为1
    窗口2卖了一张票,剩余票数为0
    窗口1卖了一张票,剩余票数为2
    

    仍然不能满足要求,这种不符合常理的结果,没有达到预想中的4 3 2 1 0
    这就是线程的交互执行导致的;举个例子:
    线程1先执行卖了1张票,也即是票--1,现在票为4,但是这个线程还没没有来得及在控制台打印出剩余多少票,线程又抢到了CPU资源执行,线程2又把票--1;此时票为3,线程2输出票就为3,线程2执行完了后,线程1又再次获得CPU资源,继续把刚刚没有输出的话输出,但是此时票已经为3了,于是又输出了3。
    看过多线程的小伙伴应该不难理解,这就是线程不安全,想要保证输出结果,可以使用synchronized关键字来解决

    2.3 实现

    public class TicketsRunnable {
    
        public static void main(String[] args) {
            MyThread mThread = new MyThread();
            Thread thread1 = new Thread(mThread,"窗口1");
            Thread thread2 = new Thread(mThread,"窗口2");
            Thread thread3 = new Thread(mThread,"窗口3");
            
            thread1.start();
            thread2.start();
            thread3.start();
        }
    
    }
    
    class MyThread implements Runnable {
        private int ticketsCont = 10;
        
        @Override
        public void run() {
            while (ticketsCont > 0) {
                //有票就卖一张
                synchronized (this) {
                    if (ticketsCont > 0) {
                        ticketsCont--;
                        System.out.println(Thread.currentThread().getName()+"卖了一张票,剩余票数为"+ ticketsCont);
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            
        }
    }
    

    用同步锁来实现,注意在锁里也加入判断语句,不然可能会出现线程1进入了while循环,然后被抢去了线程,最后卖出了51张票。

    运行结果

    窗口1卖了一张票,剩余票数为9
    窗口3卖了一张票,剩余票数为8
    窗口3卖了一张票,剩余票数为7
    窗口2卖了一张票,剩余票数为6
    窗口2卖了一张票,剩余票数为5
    窗口2卖了一张票,剩余票数为4
    窗口3卖了一张票,剩余票数为3
    窗口3卖了一张票,剩余票数为2
    窗口1卖了一张票,剩余票数为1
    窗口3卖了一张票,剩余票数为0
    
    

    三、线程的生命周期

    3.1 创建

    Thread thd = new Thread();

    3.2 就绪

    start()被调用即进入就绪状态,线程被加入到了线程队列中,等待CPU服务,具备了运行的条件,但不一定已经开始运行了

    3.3 运行

    获取到了CPU服务,执行run()逻辑

    3.4 阻塞

    受到阻塞事件的影响,由于某种原因,让出cpu资源,如sleep()方法

    3.5 终止

    run()执行完毕

    四、守护线程

    4.1 线程分类

    1. 用户线程:
      看得到的,主线程、连接网络的子线程等。
    2. 守护线程:
      运行在后台,为用户线程服务。
      特点:一旦所有用户线程结束运行,守护线程会随着JVM一起结束工作。
      应用:数据库连接池中的监测线程 & JVM虚拟机启动后的监测线程 & 垃圾回收线程

    4.2注意

    1. 在start()前调用方法 setDaemon(true)
    2. 守护线程中产生的新线程也是守护线程
    3. 不是所有的任务都可以分配给守护线程来执行,比如读写操作、逻辑运算。

    4.3守护线程中进行读写操作

    public class DaemonThreadDemo  {
    
    
        public static void main(String[] args) {
            System.out.println("进入主线程" + Thread.currentThread().getName());
            DaemonThread daemonThread = new DaemonThread();
            Thread thread = new Thread(daemonThread);
            thread.setDaemon(true);
            thread.start();
            
            Scanner scanner  = new Scanner(System.in);
            scanner.next();
            
            System.out.println("退出主线程" + Thread.currentThread().getName());
        }
    
    }
    
    class DaemonThread implements  Runnable {
        public void run() {
            System.out.println("程序进入了守护线程" + Thread.currentThread().getName());
            try {
                writeToFile();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("程序退出了守护线程" + Thread.currentThread().getName());
        }
    
        private void writeToFile() throws Exception {
            File file = new File("G:"+ File.separator+ "demo.txt");
            //true 追加操作,不是覆盖操作
            OutputStream os = new FileOutputStream(file,true);
            int count = 0;
            while (count < 999) {
                os.write(("\r\nword"+count).getBytes());
                System.out.println("守护线程"+ Thread.currentThread().getName()+"向文件中写入了word"+ count++);
                Thread.sleep(1000);
            }
            os.close();
        }
    }
    

    运行结果

    进入主线程main
    程序进入了守护线程Thread-0
    守护线程Thread-0向文件中写入了word0
    守护线程Thread-0向文件中写入了word1
    守护线程Thread-0向文件中写入了word2
    8
    退出主线程main
    

    守护线程未正常退出

    原文链接:Thread与Runnable比较

    相关文章

      网友评论

          本文标题:Java——Thread VS Runnable

          本文链接:https://www.haomeiwen.com/subject/ablupxtx.html