Java——Thread VS Runnable

一、线程创建的两种方式

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比较

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容

  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,952评论 1 18
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,448评论 1 15
  • 下面是我自己收集整理的Java线程相关的面试题,可以用它来好好准备面试。 参考文档:-《Java核心技术 卷一》-...
    阿呆变Geek阅读 14,788评论 14 507
  • 写在前面的话: 这篇博客是我从这里“转载”的,为什么转载两个字加“”呢?因为这绝不是简单的复制粘贴,我花了五六个小...
    SmartSean阅读 4,717评论 12 45
  • 桌上的菜还很多,春晓吃完饭,可小余的饭没怎么动。问小余:“你怎么不吃?”小余眼镜都不抬一下,冷冷地说:“我不饿。”...
    追光小强阅读 134评论 1 1