JAVA多线程Thread VS Runnable详解
进程与线程
进程是程序在处理机中的一次运行。一个进程既包括其所要执行的指令,也包括了执行指令所需的系统资源,不同进程所占用的系统资源相对独立。所以进程是重量级的任务,它们之间的通信和转换都需要操作系统付出较大的开销。
线程是进程中的一个实体,是被系统独立调度和分派的基本单位。线程自己基本上不拥有系统资源,但它可以与同属一个进程的其他线程共享进程所拥有的全部资源。所以线程是轻量级的任务,它们之间的通信和转换只需要较小的系统开销。
Java支持多线程编程,因此用Java编写的应用程序可以同时执行多个任务。Java的多线程机制使用起来非常方便,用户只需关注程序细节的实现,而不用担心后台的多任务系统。
Java语言里,线程表现为线程类。Thread线程类封装了所有需要的线程操作控制。在设计程序时,必须很清晰地区分开线程对象和运行线程,可以将线程对象看作是运行线程的控制面板。在线程对象里有很多方法来控制一个线程是否运行,睡眠,挂起或停止。线程类是控制线程行为的唯一的手段。一旦一个Java程序启动后,就已经有一个线程在运行。可通过调用Thread.currentThread方法来查看当前运行的是哪一个线程。
线程创建的两种方法
JAVA中创建线程可以通过继承Thread类和实现Runnable接口来创建一个线程。Runnable方式可以避免Thread 方式由于JAVA单继承特性带来的缺陷。Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况。
继承Thread类
class MyThread extends Thread{
.....
@Override
public void run(){
}
}
MyThread mt=new MyThread();
mt.start();
实现Runnable接口
class MyThread implements Runnable{
....
@Override
public void run(){
}
}
MyThread mt=new MyThread();
Thread td=new Thread(mt);
sd.start();
Thread&Runnable分别模拟卖火车票
Thread方式
class MyThread extends Thread
{
private int ticketsCont=5; //一共有5张火车票 private String name; //窗口, 也即是线程的名字 public MyThread(String name){
this.name=name;
} @Override
public void run(){ while(ticketsCont>0){
ticketsCont--; //如果还有票,就卖掉一张票
System.out.println(name+"卖掉了1张票,剩余票数为:"+ticketsCont);
} }
} public class TicketsThread
{
public static void main(String args[]){ //创建三个线程,模拟三个窗口卖票
MyThread mt1=new MyThread("窗口1");
MyThread mt2=new MyThread("窗口2");
MyThread mt3=new MyThread("窗口3"); //启动三个线程,也即是窗口,开始卖票
mt1.start();
mt2.start();
mt3.start(); }
}
Runnable方式
class MyThread2 implements Runnable
{
private int ticketsCont=1000; //一共有5张火车票 @Override
public void run(){ while(true){
synchronized(this){
if(ticketsCont<=0){
break;
}
ticketsCont--; //如果还有票,就卖掉一张票 System.out.println(Thread.currentThread().getName()+"卖掉了1张票,剩余票数为:"+ticketsCont); /*try{
Thread.sleep(50); //通过阻塞程序来查看效果
}
catch(Exception e){
System.out.println(e);
}*/ }
} } /*@Override //不加同步锁
public void run(){
while(ticketsCont>0){
ticketsCont--; //如果还有票,就卖掉一张票
System.out.println(Thread.currentThread().getName()+"卖掉了1张票,剩余票数为:"+ticketsCont);
}
}*/
} public class TicketsRunnable
{
public static void main(String args[]){ MyThread2 mt=new MyThread2();
//创建三个线程来模拟三个售票窗口
Thread th1=new Thread(mt,"窗口1");
Thread th2=new Thread(mt,"窗口2");
Thread th3=new Thread(mt,"窗口3"); //启动三个线程,也即是三个窗口,开始卖票
th1.start();
th2.start();
th3.start(); }
}
线程的生命周期

- 创建:新建一个线程对象,如Thread thd=new Thread()
- 就绪:创建了线程对象后,调用了线程的start()方法(此时线程知识进入了线程队列,等待获取CPU服务 ,具备了运行的条件,但并不一定已经开始运行了)
- 运行:处于就绪状态的线程,一旦获取了CPU资源,便进入到运行状态,开始执行run()方法里面的逻辑
- 终止:线程的run()方法执行完毕,或者线程调用了stop()方法,线程便进入终止状态
- 阻塞:一个正在执行的线程在某些情况系,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法
线程的分类
- 用户线程:运行在前台,执行具有的任务程序的主线程,连接网络的子线程等都是用户线程
- 守护线程:运行在后头,为其他前台线程服务。一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作。可以通过调用Thread类的setDaemon(true)方法来设置当前的线程为守护线程,该方法必须在start()方法之前调用,否则会抛出 IllegalThreadStateException异常。在守护线程中产生的新线程也是守护线程。不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑。
守护线程测试案例

场景:一个主线程,一个守护线程,守护线程会在很长一段时间内向本地文件中写入数据,主线程进入阻塞状态等待用户的输入,一旦确认了用户的输入阻塞就会解除掉,主线程继续运行直到结束,守护线程也会随虚拟机一同结束。
import java.io.*;
import java.util.Scanner; class Daemon implements Runnable
{
@Override
public void run(){
System.out.println("进入守护线程");
try{
writeToFile();
}
catch(Exception e){
e.printStackTrace();
} System.out.println("退出守护线程");
} private void writeToFile() throws Exception{
File filename=new File("F:/慕课网(imooc)/细说多线程之Thread VS Runnable/daemon.txt");
OutputStream os=new FileOutputStream(filename,true);
int count=0;
while(count<999){
os.write(("\r\nword"+count).getBytes());
System.out.println("守护线程"+Thread.currentThread().getName()+"向文件中写入word"+count++);
Thread.sleep(1000);
}
}
} public class DaemonThreadDemo
{
public static void main(String args[]){
System.out.println("进入主线程"+Thread.currentThread().getName());
Daemon daemonThread=new Daemon();
Thread thread =new Thread(daemonThread);
thread.setDaemon(true);
thread.start(); Scanner sc=new Scanner(System.in);
sc.next(); System.out.println("退出主线程"+Thread.currentThread().getName()); }
}
测试结果

使用jstack生成线程快照
jstack工具到jdk安装目录bin文件夹下。jstack能生成JVM当前时刻线程的快照(threaddump, 即当前进程中所有线程的信息)。帮助定位程序问题出现的原因,如长时间停顿、CPU占用率过高等。
使用方法
jstack [-l] <pid> : [-l]可有可无,表示关于锁的二位信息;<pid>表示进程ID。

总结
建议使用Runnable这种方式创建线程。 程序中的同一资源指的是同一个Runnable对象。安全的卖票程序中需要加入同步synchronized。
如以上文章或链接对你有帮助的话,别忘了在文章结尾处轻轻点击一下 “还不错”按钮或到页面右下角点击 “赞一个” 按钮哦。你也可以点击页面右边“分享”悬浮按钮哦,让更多的人阅读这篇文章。
JAVA多线程Thread VS Runnable详解的更多相关文章
- Java多线程之线程池详解
前言 在认识线程池之前,我们需要使用线程就去创建一个线程,但是我们会发现有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因 ...
- java多线程及线程安全详解
为什么要使用多线程: 单线程只能干一件事 而多线程可以同时干好多事(将任务放到线程里执行 效率高) 而所谓同时干并不是真正意义上的同时 只是(这里就叫CPU)cpu在每个线程中随机切换来执行 ...
- Java多线程中join方法详解
join()方法用于让当前执行线程等待join线程执行结束.其实现原理是不停的检查join线程是否存活,如果join线程存活则让当前线程永远等待. join()方法部分实现细节 while(isAli ...
- Java 多线程同步和异步详解
java线程 同步与异步 线程池 1)多线程并发时,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线 程的处理的数据,而B线程又修改了A线程处理的数理.显然这是由于全局资源造成 ...
- java.lang.Thread.State类详解
public static enum Thread.Stateextends Enum<Thread.State>线程状态.线程可以处于下列状态之一: 1.NEW 至今尚未启动的线程的状态 ...
- java 多线程Thread和Runnable的区别
如果一个类继承Thread,则不适合资源共享.但是如果实现了Runable接口的话,则很容易的实现资源共享 实现Runnable接口比继承Thread类所具有的优势:1. 适合多个相同的程序代码的线程 ...
- Java 多线程Thread和Runnable
Thread: class MyThread extends Thread { private int ticketsCont=5; //一共有5张火车票 private String name; / ...
- java多线程——同步块synchronized详解
Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...
- JAVA多线程Thread与Runnable
一.Runnable Runnable为一个之包含一个run方法的接口 public class MyRunnable implements Runnable{ @Override //表示:预示重写 ...
随机推荐
- FatMouse's Speed (hdu 1160)
#include <iostream> #include <cstdio> #include <cstring> #include <algori ...
- 软件工程课堂练习&课下作业
题目:返回一个整数数组中最大子数组的和.一.设计思路按顺序定义子数组的和,如果为负,则选下一位数为子数组的和,反之则两个相加为子数组的和.二.源代码 package test;import java. ...
- Lib作为“静态库”与“动态库”中的区别
Lib作为“静态库”与“动态库”中的区别 0. 前言: 什么是静态连接库: 静态库在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中.因此对应的链接方式称为静态链接. 为什么 ...
- 方案dp。。
最近经常做到组合计数的题目,每当看到这种题目第一反应总是组合数学,然后要用到排列组合公式,以及容斥原理之类的..然后想啊想,最后还是不会做.. 但是比赛完之后一看,竟然是dp..例如前几天的口号匹配求 ...
- django后台admin管理布局
在model模块里设置 class pc_info(models.Model): ip = models.CharField(max_length=64) sn = models.CharField( ...
- RxSwift学习笔记1:RxSwift的编程风格
第一天:简单体验与RxSwift的编程风格 import UIKit//导入Rx相关框架 import RxSwift import RxCocoa struct Music { let name:S ...
- Dnsmasq安装与配置-搭建本地DNS服务器
默认的情况下,我们平时上网用的本地DNS服务器都是使用电信或者联通的,但是这样也导致了不少的问题,首当其冲的就是上网时经常莫名地弹出广告,或者莫名的流量被消耗掉导致网速变慢.其次是部分网站域名不能正常 ...
- 为什么在UDP包中不能获取发包方的地址
首先,我们要先了解一下UDP包的结构. 图1 UDP报文格式 从图1,我们可以看出,从UDP包中,我们可以获取的信息只有源端口和目的地端口.我们不能获取到源IP因为报文中没有源IP.真正包含IP地址的 ...
- Python学习--和 Oracle 交互
python 连接oracle 数据库 1.安装 cx_oracle pip install cx_oracle 2.出现 cx_Oracle.DatabaseError: DPI-1047: 64- ...
- docker容器中的peewee如何连接已有的容器中的数据库
首先,两个容器必须是在同一网络下,有2个办法. 一个是在同一个docker-compose.yml文件下使用links参数,比如: version: '3' services: redis: imag ...