一线程创建的两种方式比较

线程创建和启动有两种方式,这里只是列出步骤,不再进行详细解释。

(1)继承Thread类

[java] view plaincopy print?
  1. class MyThread extends Thread{
  2. public void run(){
  3. ...
  4. }
  5. }
  6. MyThread mt=new MyThread();//创建线程
  7. mt.start();//启动线程

(2)实现Runnable接口

[java] view plaincopy print?
  1. class MyThread implements Runnable{
  2. public void run(){
  3. ...
  4. }
  5. }
  6. MyThread mt=new MyThread();
  7. Thread td=new Thread(mt);//创建线程
  8. td.start();//启动线程

(3)两种方式的比较

1)Runnable方式可以避免Thread方式由于Java单继承特性带来的缺陷。

2)Runnable方式的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况。

二模拟应用场景

模拟一个火车站买票的场景,某车次还剩下5张火车票,有三个窗口去卖这5张火车票,我们使用三个线程模拟三

个窗口同时卖这5张火车票,我们看Thread方式和Runnable方式这两种方式模拟出一个什么样的结果。

(1)使用Thread方式模拟买票

TicketsThread.java源文件代码:

[java] view plaincopy print?
  1. class MyThread extends Thread{
  2. //一共有五张火车票
  3. private int ticketsCount = 5;
  4. //窗口,也就是线程的名字
  5. private String name;
  6. //构造方法
  7. public MyThread(String name){
  8. this.name = name;
  9. }
  10. public void run(){
  11. while(ticketsCount > 0){
  12. //如果还有票,就卖掉一张
  13. ticketsCount--;
  14. System.out.println(name+"卖了1张票,剩余票数为:"+ticketsCount);
  15. }
  16. }
  17. }
  18. public class TicketsThread{
  19. public static void main(String[] args){
  20. //创建三个线程,模拟三个窗口买票
  21. MyThread mt1 = new MyThread("窗口1");
  22. MyThread mt2 = new MyThread("窗口2");
  23. MyThread mt3 = new MyThread("窗口3");
  24. //启动三个线程,也就是窗口开始卖票
  25. mt1.start();
  26. mt2.start();
  27. mt3.start();
  28. }
  29. }

运行结果:

得到的结果并不是我们想要的结果。

在票的数量加static修饰关键字得到的结果是正确的。这里不再进行演示。

(2)使用Runnable方式模拟买票

TicketsRunnable.java源文件代码:

[java] view plaincopy print?
  1. class MyThread1 implements Runnable{
  2. //一共有五张火车票
  3. private int ticketsCount = 5;
  4. public void run(){
  5. while(ticketsCount > 0){
  6. //如果还有票,就卖掉一张
  7. ticketsCount--;
  8. System.out.println(Thread.currentThread().getName()+"卖了1张票,剩余票数为:"+ticketsCount);
  9. }
  10. }
  11. }
  12. public class TicketsRunnable{
  13. public static void main(String[] args){
  14. MyThread1 mt = new MyThread1();
  15. //创建三个线程,模拟三个窗口买票
  16. Thread th1 = new Thread(mt,"窗口1");
  17. Thread th2 = new Thread(mt,"窗口2");
  18. Thread th3 = new Thread(mt,"窗口3");
  19. //启动三个线程,也就是窗口开始卖票
  20. th1.start();
  21. th2.start();
  22. th3.start();
  23. }
  24. }

得到了预期的结果:

(3)结果分析

由于线程的执行是随机的,打印的结果也是随机的。

Thread方式

三个线程,创建了三个Thread对象,每个线程都有自己的Thread对象,都有自己的ticketsCount变量,它们三个

线程并不是共享ticketsCount变量,也就是每个线程都可以卖出5张火车票,即三个窗口卖出去15张火车票。

Runnable方式

三个线程共用一个Runnable对象,也就是三个线程共用一个ticketsCount变量,即三个窗口一共卖了5张火车票。

前者不是一个线程三个对象,是三个Thread对象,也是三个线程,这三个线程启动后都会执行5次卖票,实现不

了共享“5张票”这个资源,所以输出就会有15张票卖出去,显然不符合实际,用Runnable就可以解决这个问题,创建

的三个线程可以共享"5张票"这个资源。

ticketsCont变量是实例变量,它的值自然是存在堆中(每个java对象在堆中都会占据一定内存,而实例变量的值就

是存储在这块内存中,类似于结构体,因此每个对象对应一个ticketsCont的值),ticketsCont跟值传递没有关系啊,如

果是Runnable方式的话,传递的也只是MyThread对象引用的副本,不管ticketsCont的事,但是因为ticketsCont的值

在引用和引用副本所指向的堆内存中,所以无论是引用还是引用副本改变了堆内存中ticketsCont的值,都会产生效

果!

这个根据你的需要来操作,这样说吧,如果有一个比较大的资源要你下载,那么你用Thread方式那么你就只能一

个线程去吧这个资源下载完,如果是runable方式的话你就可以 多new几个子线程来出来,通过共享runable对象里面

的资源来用多个子线程来下载这个资源,这样的话,下载资源的时候 runable方法会使下载的线程多一些几率在cpu里

面,也会让你下载速度变快继承Thread类是多个线程分别完成自己的任务,实现Runnable接口是多个线程共同完成

一个任务。

三线程的生命周期

线程的生命周期转换示意图:

线程的生命周期:

1)创建:新建一个线程对象,如Thread thd=new Thread()。

2)就绪:创建了线程对象后,调用了线程的start()方法(注意:此时线程只是进入了线程队列,等待获取CPU服

务,具备了运行的条件,但并不一定已经开始运行了)。

3)运行:处于就绪状态的线程,一旦获取了CPU资源,便进入到运行状态,开始执行run()方法里面的逻辑。

4)阻塞:一个正在执行的线程在某些情况下,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入

了阻塞状态,如调用了sleep()方法。

5)终止:线程的run()方法执行完毕,或者线程调用了stop()方法,线程便进入终止状态。

这里我们可以用一个经典的线问题就是生产者和消费者问题的实例:

[java] view plaincopy print?
  1. import java.util.*;
  2. public class ProducerConsumer{
  3. public static void main(String[] args){
  4. SyncStack ss = new SyncStack();
  5. Producer p = new Producer(ss);
  6. Consumer c = new Consumer(ss);
  7. new Thread(p).start();
  8. new Thread(c).start();
  9. }
  10. }
  11. //生产与消费对象
  12. class WoTou{
  13. int id;
  14. //构造方法
  15. WoTou(int id){
  16. this.id = id;
  17. }
  18. //重写toString()方法
  19. public String toString(){
  20. return "WoTou : " + id;
  21. }
  22. }
  23. //容器类
  24. class SyncStack{
  25. int index = 0;
  26. WoTou[] arrWT = new WoTou[4];
  27. public synchronized void push(WoTou wt){
  28. while(index == arrWT.length){
  29. try{
  30. //这里的wait()方法指的是Object类中的方法
  31. this.wait();
  32. }catch(InterruptedException e){
  33. e.printStackTrace();
  34. }
  35. }
  36. //这里的notifyAll()方法指的是唤醒所有线程,而notify()方法唤醒一个线程
  37. this.notifyAll();
  38. arrWT[index] = wt;
  39. index ++;
  40. }
  41. public synchronized WoTou pop(){
  42. while(index == 0){
  43. try{
  44. //这里的wait()方法指的是Object类中的方法
  45. this.wait();
  46. }catch(InterruptedException e){
  47. e.printStackTrace();
  48. }
  49. }
  50. //这里的notifyAll()方法指的是唤醒所有线程,而notify()方法唤醒一个线程
  51. this.notifyAll();
  52. index--;
  53. return arrWT[index];
  54. }
  55. }
  56. //生产者
  57. class Producer implements Runnable{
  58. SyncStack ss = null;
  59. Producer(SyncStack ss){
  60. this.ss = ss;
  61. }
  62. public void run(){
  63. for(int i=0; i<20; i++){
  64. WoTou wt = new WoTou(i);
  65. ss.push(wt);
  66. System.out.println("生产了:" + wt);
  67. try{
  68. Thread.sleep((int)(Math.random() * 200));
  69. }catch(InterruptedException e){
  70. e.printStackTrace();
  71. }
  72. }
  73. }
  74. }
  75. //消费者
  76. class Consumer implements Runnable{
  77. SyncStack ss = null;
  78. Consumer(SyncStack ss){
  79. this.ss = ss;
  80. }
  81. public void run(){
  82. for(int i=0; i<20; i++){
  83. WoTou wt = ss.pop();
  84. System.out.println("消费了: " + wt);
  85. try{
  86. Thread.sleep((int)(Math.random() * 1000));
  87. }catch(InterruptedException e){
  88. e.printStackTrace();
  89. }
  90. }
  91. }
  92. }

运行结果:

关于一些问题的解析:

执行线程sleep()方法是依然占着cpu的,操作系统认为该当前线程正在运行,不会让出系统资源。

执行wait()方法是让线程到等待池等待,让出一系列的系统资源,其他线程可以根据调度占用cpu线程的资源有不

少,但应该包含CPU资源和锁资源这两类。

sleep(long mills):让出CPU资源,但是不会释放锁资源。

wait():让出CPU资源和锁资源。

锁是用来线程同步的,sleep(long mills)虽然让出了CPU,但是不会让出锁,其他线程可以利用CPU时间片了,但

如果其他线程要获取sleep(long mills)拥有的锁才能执行,则会因为无法获取锁而不能执行,继续等待。但是那些没有

和sleep(long mills)竞争锁的线程,一旦得到CPU时间片即可运行了。

四守护线程

(1)守护线程理论知识

Java线程分为两类:

1)用户线程:运行在前台,执行具体的任务。程序的主线程、连接网络的子线程等都是用户线程。

2)守护线程:运行在后台,为其他前台线程服务。守护线程的特点是一旦所有用户线程都结束运行,守护线程会

随JVM一起结束工作。守护线程的应用:数据库连接池中的检测线程和JVM虚拟机启动后的检测线程等等。最常见的

守护线程:垃圾回收线程

设置守护线程:

可以通过调用Thread类的setDaemon(true)方法来设置当前的线程为守护线程。

使用守护线程的注意事项:

1)setDaemon(true)必须在start()方法之前调用,否则会抛出IllegalThreadStateException异常。

2)在守护线程中产生的新线程也是守护线程。

3)不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑。

(2)守护线程代码示例:

[java] view plaincopy print?
  1. import java.io.*;
  2. import java.util.*;
  3. class DaemonThread implements Runnable{
  4. public void run(){
  5. System.out.println("进入守护线程"+Thread.currentThread().getName());
  6. try{
  7. writeToFile();
  8. }catch(Exception e){
  9. e.printStackTrace();
  10. }
  11. System.out.println("退出守护线程");
  12. }
  13. private void writeToFile() throws Exception{
  14. File filename = new File("E:\\Java\\JavaSE\\Thread"+File.separator+"daemon.txt");
  15. OutputStream os = new FileOutputStream(filename,true);
  16. int count = 0;
  17. while(count < 999){
  18. os.write(("\r\nword"+count).getBytes());
  19. System.out.println("守护线程"+Thread.currentThread().getName()+"向文件中写入了word"+ count++);
  20. Thread.sleep(1000);
  21. }
  22. }
  23. }
  24. public class DaemonThreadDemo{
  25. public static void main(String[] args){
  26. System.out.println("进入主线程"+Thread.currentThread().getName());
  27. DaemonThread daemonThread = new DaemonThread();
  28. Thread thread =new Thread(daemonThread);
  29. thread.setDaemon(true);
  30. thread.start();
  31. Scanner sc = new Scanner(System.in);
  32. sc.next();
  33. System.out.println("退出主线程"+Thread.currentThread().getName());
  34. }
  35. }

运行结果:

(3)使用jstack生成线程快照

作用:生成JVM当前时刻线程的快照(threaddump,即当前进程中所有线程的信息)。

目的:帮助定位程序问题出现的原因,如长时间停顿、CPU占用率高等。

jstack命令行工具

你安装JDK安装目录下的bin文件夹下:

位置:E:\Java\develop\jdk1.8.0_25\bin

如何使用jstack

在cmd中输入:jstack

我们去任务管理器中找到一个进程的PID:

生成快照:

五总结

(1)怎样解决死锁的问题

1)尽量避免不必要的synchronized关键字。

2)可以用其他方法替换synchronized关键字,比如标志不可变量。

3)保证synchronized代码块简练。

(2)创建线程的建议

根据两种创建线程方法的比较,得出的结论是使用实现Runnable接口的方法创建的多线程更好一些,另外,就是

需要重点注意,程序中的同一个资源指的是同一个Runnable对象。建议多使用Runnable这种方式创建多线程。

(3)补充:

1)程序中的同一资源指的是同一个Runnable对象。

2)安全的卖票程序中需要加入同步(Synchronized)。我们的代码过程中并没有加入,如果需要完善,就必须加入

同步锁。

from: http://blog.csdn.net/erlian1992/article/details/51707369

JavaSE学习52:细说多线程之Thread类和Runable接口相关推荐

  1. Thread类和Runable接口使用

    不废话,就一个类,直接拷贝代码执行 1 package com.jtfr.demo; 2 3 /** 4 * 主要:继承 Thread 类和 Runnable接口 5 * @author 陈康明 qq ...

  2. 多线程之Thread类

    Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知 ...

  3. C# 多线程之Thread类

    // 摘要: // 初始化 System.Threading.Thread 类的新实例,指定允许对象在线程启动时传递给线程的委托.//// 参数: // start:// System.Threadi ...

  4. JAVA线程之Thread类详解

    Thread类用于操作线程,是所以涉及到线程操作(如并发)的基础.本文将通过源码对Thread类的功能作用进行分析. 一.属性 /* Make sure registerNatives is the ...

  5. java thread类_java多线程之Thread类

    Class Thread java.lang.Object java.lang.Thread 实现接口:Runnable 直接被继承的子类:ForkJoinWorkerThread public cl ...

  6. 稳扎稳打Silverlight(25) - 2.0线程之Thread, Timer, BackgroundWorker, ThreadPool

    [索引页] [源码下载] 稳扎稳打Silverlight(25) - 2.0线程之Thread, Timer, BackgroundWorker, ThreadPool 作者:webabcd 介绍 S ...

  7. Java多线程的实现方式-Thread 类,Runnable 接口

    在 Java 的 JDK 开发包中,已经自带了对多线程技术的支持,可以方便地进行多线程编程.实现多线程编程的方式主要有两种:一种是继承 Thread 类,另一种是实现 Runnable 接口.下面详细 ...

  8. Java多线程和并发(三),Thread类和Runnable接口

    目录 1.Thread和Runnable接口 三.Thread类和Runnable接口 1.Thread和Runnable接口 转载于:https://www.cnblogs.com/xzmxddx/ ...

  9. (一)java多线程之Thread

    目录 Thread类 创建一个线程 java创建线程有两种方式 实现 区别 让线程"睡"一会 停止线程 线程的属性 Thread.join() Thread.yield 线程的异常 ...

最新文章

  1. 在 SharePoint2010 中使用 XML 查看器取得其他站点的内容列表
  2. solidworks模板_工程师实用高级操作,Solidworks自定义属性链接到工程图模板
  3. 产品经理如何应对一句话需求
  4. python日志处理_Python之日志处理(logging模块)
  5. mysql show db_mysql show操作
  6. IBM携手MIT组建新实验室:人工智能将有像人一样的视听功能
  7. Centos挂载新硬盘开机自动挂载
  8. 20210311 plecs 对传递函数进行波特图分析
  9. 京瓷1800打印机扫描步骤_京瓷复印机扫描设置方法京瓷复印机扫描到电脑设置...
  10. 17.3 构建LinuxPC端QT软件上的ARM编译套件并进行测试
  11. 移动硬盘启动win7蓝屏7b_win7启动蓝屏0X0000007B代码的解决方法
  12. iis 支持apk json ipa下载
  13. 基于蜜蜂算法的函数寻优及TSP搜索算法
  14. 180724 安卓-SSLPinning及反制
  15. [2016 版] 常见操作性能对比
  16. 【宋红康 MySQL数据库 】【高级篇】【12】性能分析工具的使用
  17. 2093321-19-6,PSMA-1007PSMA1007特异性膜抗原(PSMA)抑制剂
  18. DBM、GDBM与C语言跨平台代码研究
  19. 解决eclipse启动Tomcat报错,显示8005和8080端口被占用的问题
  20. Julia 语言现在完成度如何?与R语言比较起来哪个更好?

热门文章

  1. keepalived程序包
  2. NUXT快速开始,选择Vuetify
  3. CCAI 2017 | 德国DFKI科技总监Hans Uszkoreit:如何用机器学习和知识图谱来实现商业智能化? 原2017.07.25AI科技大本营 文/CSDN大琦 7 月22 - 2
  4. 计算机二级考试常用代码,二级计算机VB考试常用代码(看完必过).doc
  5. error C4335: Mac file format detected: please convert the source file to either DOS or UNIX format
  6. MySQL-count(*) 和 not in 的查询优化
  7. java web临时文件删除_什么时候删除Java临时文件?
  8. executequery方法_【接口测试】soapui中数字、字符串、日期时间、数据库连接参数化的设置方法...
  9. hashmap 判断key是否存在
  10. C语言如何使用函数交换两个变量的值