Java实现多线程的两种方式比较及区别
Java实现多线程的两种方式比较及区别
Java实现多线程的方式:2种 方式1:自定义类继承Thread类 1:自定义类MyThread继承Thread类 2:MyThread类里面重写run()方法 3:在测测试类MyThreadTest中创建MyThread类的对象 4:启动线程 方式2:自定义类实现Runnable接口 1:自定义类MyRunnable实现Runnable接口 2:MyRunnable类里面重写run()方法 3:在测测试类MyRunnableTest中创建MyRunnable类的对象 4;在测测试类MyRunnableTest中再创建Thread类的对象,并把3步骤的对象作为构造参数进行传递 5:启动线程 问题: 1:为什么要重写run()方法? 答:run()方法里面封装的是被线程执行的代码。 2:启动线程对象用的是哪个方法? 答:start()方法 3:run()方法和start()方法的区别? 答:run()方法直接调用仅仅是普通方法。
start()方法是先启动线程,再由jvm去调用run()方法。 4:有了方式1,为什么还来一个方式2呢? 答:若自定义类MyThread类已经有一个父类了,那么它就不可以再去继承Thread类了。(java不支持多继承) 若自定义类MyRunnable类已经实现了一个接口了,那么它还可以再去实现Runnable接口。(java支持多实现) 即可以避免由于Java单继承带来的局限性。 在测试类MyThreadTest中,要想开多个线程,就要先new多个自定义类MyThread的对象,每一个自定义类MyThread的对象的成员变量都相同,这样需要在栈中开辟很多内存; 在测试类MyRunnableTest中,要想开多个线程,只需要new一个自定义类MyRunnable的对象,再new多个Thread类的对象即可,这样就大大节约了内存。 即适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码和数据有效分离(即耦合性降低),较好的体现了Java面向对象的设计思想。
注意:图片中 1:为什么药重写run()方法?有个错别字药,应该为要。
示例代码如下:
方式1:自定义类继承Thread类
1 package cn.itcast_02; 2 3 /* 4 * 该自定义的类为什么要重写run()方法? 5 * 自定义类中不是所有的代码都需要被线程执行。 6 * 而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()方法,用来包含那些需要被线程执行的代码。 7 * 8 * 注意:这里的 被线程执行 = 开一个新线程执行 9 */ 10 public class MyThread extends Thread { 11 // 不需要被线程执行的代码 12 public void show() { 13 System.out.println("不需要被线程执行的代码"); // 这么简单的程序去开一个新的线程,纯属浪费啊! 14 } 15 16 // 需要被线程执行的代码 17 @Override 18 public void run() { 19 // System.out.println("好好学习,天天向上"); // 这么简单的程序去开一个新的线程,纯属浪费啊! 20 21 // 一般来说,被线程执行的代码肯定是比较耗时的。 22 // 所以这里示例:我们使用循环 23 for (int x = 0; x < 1000; x++) { 24 System.out.println(x); 25 } 26 } 27 28 }
1 package cn.itcast_02; 2 3 /* 4 * 需求:我们要实现多线程的程序。 5 * 6 * 如何通过java程序来实现多线程的程序呢? 7 * 由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。 8 * 而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。 9 * 10 * 而Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。 11 * 但是呢?Java可以去调用C/C++写好的程序来间接实现多线程程序。 12 * 13 * 由C/C++程序去调用系统功能去创建进程,然后由Java进行封装后,这样会产生一些类,我们通过这些类创建的对象去调用他们即可! 14 * 15 * 这样我们就可以通过java程序来实现多线程程序了。 16 * 17 * 那么Java提供的类是什么呢? 18 * Thread类 19 * 通过查看API,我们知道了有2种方式可以实现多线程程序。 20 * (其实有三种方法,第三种明天讲) 21 * 22 * 实现多线程程序 23 * 方式1:继承Thread类 24 * 是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。之后就可以分配并启动该子类的实例了。 25 * 步骤 26 * A:自定义类MyThread继承Thread类 27 * B:MyThread类里面重写run()方法 28 * C:在测测试类MyThreadTest中创建MyThread类的对象 29 * D:启动线程 30 */ 31 public class MyThreadTest { 32 public static void main(String[] args) { 33 // 在测测试类中创建自定义类的对象 34 // MyThread my = new MyThread(); 35 // 启动线程 36 // my.run(); 37 // my.run(); 38 39 // 调用run()方法为什么是单线程的呢? 40 // 因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果。 41 42 // 要想看到多线程的效果,就必须说说另一个方法:start() 43 // 面试题:run()和start()的区别? 44 // run():仅仅是封装被线程执行的代码,直接调用就是普通方法。 45 // start():首先启动了线程,然后再由jvm去调用该线程的run()方法。 46 47 // MyThread my = new MyThread(); 48 // my.start(); 49 // my.start(); 50 // IllegalThreadStateException:非法的线程状态异常 51 // 为什么呢?因为这个相当于是my线程被调用了两次。而不是两个线程被启动。一个线程不能被多次启动。 52 53 // 创建两个线程对象 54 MyThread my1 = new MyThread(); 55 MyThread my2 = new MyThread(); 56 57 my1.setName("林青霞"); 58 my2.setName("刘意"); 59 60 my1.start(); 61 my2.start(); 62 } 63 }
方式2:自定义类实现Runnable接口
1 package cn.itcast_05; 2 3 public class MyRunnable implements Runnable { 4 5 @Override 6 public void run() { 7 for (int x = 0; x < 100; x++) { 8 // 由于自定义类实现了接口,所以就不能在自定义类中直接使用Thread类的getName()方法了,但是可以间接的使用。 9 // 间接方法:Thread.currentThread().getName() 10 System.out.println(Thread.currentThread().getName() + ":" + x); 11 } 12 } 13 14 }
1 package cn.itcast_05; 2 3 /* 4 * 实现多线程程序 5 * 方式2:实现Runnable接口 6 * 步骤: 7 * A:在自定义类MyRunnable实现Runnable接口 8 * B:MyRunnable类里面重写run()方法 9 * C:在测测试类MyRunnableTest中创建MyRunnable类的对象 10 * D:在测测试类MyRunnableTest中再创建Thread类的对象,并把C步骤的对象作为构造参数进行传递 11 * E:启动线程 12 */ 13 public class MyRunnableTest { 14 public static void main(String[] args) { 15 // 在测测试类中创建MyRunnable类的对象 16 MyRunnable my = new MyRunnable(); 17 18 // public Thread(Runnable target) 19 // 在测测试类中再创建Thread类的对象,并把C步骤的对象作为构造参数进行传递 20 // Thread t1 = new Thread(my); 21 // Thread t2 = new Thread(my); 22 // t1.setName("林青霞"); 23 // t2.setName("刘意"); 24 25 // Thread类的方法: 26 // public Thread(Runnable target, String name) 27 Thread t1 = new Thread(my, "林青霞"); 28 Thread t2 = new Thread(my, "刘意"); 29 30 t1.start(); 31 t2.start(); 32 } 33 }
我的GitHub地址: https://github.com/heizemingjun
我的博客园地址: http://www.cnblogs.com/chenmingjun
我的蚂蚁笔记博客地址: http://blog.leanote.com/chenmingjun
Copyright ©2018 黑泽明军
【转载文章务必保留出处和署名,谢谢!】
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
线程的控制之守护线程_坦克大战帮助理解
后台线程 public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程 对象名.setDaemon(true); // 设置守护线程 当正在运行的线程都是守护线程时,Java虚拟机退出。该方法必须在启动线程前调用。 坦克大战帮助理解守护线程,如下图所示: 示例代码如下: 1 package cn.itcast_04; 2 3 /* 4 * 后台线程 5 * public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。 6 * 当正在运行的线程都是守护线程时,Java虚拟机退出。 该方法必须在启动线程前调用。 7 * 8 * 帮助理解的守护线程的案例 9 * 游戏:坦克大战。 10 */ 11 public class ThreadDaemonDemo { 12 public static void main(String[] args) { 13 ThreadDaemon td1 = new ThreadDaemon(); 14 ThreadDaemon td2 = new Thr...
- 下一篇
腾讯2017秋招笔试编程题--游戏任务标记 java 实现+ c 实现
时间限制:1秒 空间限制:32768K 游戏里面有很多各式各样的任务,其中有一种任务玩家只能做一次,这类任务一共有1024个,任务ID范围[1,1024]。请用32个unsigned int类型来记录着1024个任务是否已经完成。初始状态都是未完成。 输入两个参数,都是任务ID,需要设置第一个ID的任务为已经完成;并检查第二个ID的任务是否已经完成。 输出一个参数,如果第二个ID的任务已经完成输出1,如果未完成输出0。如果第一或第二个ID不在[1,1024]范围,则输出-1。 输入描述: 输入包括一行,两个整数表示人物ID. 输出描述: 输出是否完成 输入例子1: 1024 1024 输出例子1: 1 分析 题目要求用32个 unsigned int 类型来记录1024个数,每个数有两种状态,0或者1,0代表这个任务没完成,1 代表这个任务未完成。这里1024个数,用32个unsigned int 表示,那么如果 1024/32 = 32,每一个unsigned int 要表示32个数(相当于32种状态位),unsigned int 占4个字节,那么这4个字节用位来表示32中状态(4个...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8安装Docker,最新的服务器搭配容器使用
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2配置默认Tomcat设置,开启更多高级功能