Java多线程--细说多线程之Thread & Runnable
细说多线程之Thread & Runnable
对应的慕课网课程地址
Thread & Runable
线程创建的两种方式
- 继承Thread 类
class MyThread extends Thread{
....
@Override
public void run(){
...
}
}
MyThread mt = new MyThread(); // 创建线程
mt.start(); // 启动线程
- 实现Runable 接口:
class MyThread implement Runnable{
...
@Override
public run(){
...
}
}
MyThread mt = new MyThread();
Thread td = new Thread(mt); // 创建线程
td.start(); // 启动线程
Runnable 方式可以避免Thread 方式由于Java单继承特性带来的缺陷;
Runnable 的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况。
用Thread 和Runnable 模拟买票场景
使用Thread实现
package com.mooc.thread;
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 +" remaining :" + ticketsCont);
}
}
}
public class TicketsThread {
public static void main(String[] args){
// 创建三个线程模拟三个窗口买票
MyThread mt1 = new MyThread("Window 1");
MyThread mt2 = new MyThread("Window 2");
MyThread mt3 = new MyThread("Window 3");
// 启动线程
mt1.start();
mt2.start();
mt3.start();
}
}
使用Runnable实现
线程共享同一个【实例】的资源
package com.mooc.runnable;
/**
* 线程共享同一个实例的资源
*/
class MyThread extends Thread{
private int ticketsCont = 5; // 火车票的数量
@Override
public void run() {
while (ticketsCont >0){
ticketsCont--;
System.out.println(Thread.currentThread().getName() +" remaining :" + ticketsCont);
}
}
}
public class TicketsRunnable {
public static void main(String[] args){
// 创建三个线程模拟三个窗口买票
MyThread mt1 = new MyThread();
Thread td1 = new Thread(mt1,"Window 1");
Thread td2 = new Thread(mt1,"Window 2");
Thread td3 = new Thread(mt1,"Window 3");
// 启动线程
td1.start();
td2.start();
td3.start();
}
}
资源不共享
package com.mooc.runnable;
/**
* 资源不共享
*/
class MyThread extends Thread{
private int ticketsCont = 5; // 火车票的数量
@Override
public void run() {
while (ticketsCont >0){
ticketsCont--;
System.out.println(Thread.currentThread().getName() +" remaining :" + ticketsCont);
}
}
}
public class TicketsRunnable {
public static void main(String[] args){
// 创建三个线程模拟三个窗口买票
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
Thread td1 = new Thread(mt1,"Window 1");
Thread td2 = new Thread(mt2,"Window 2");
Thread td3 = new Thread(mt3,"Window 3");
// 启动线程
td1.start();
td2.start();
td3.start();
}
}
线程的生命周期
创建: 新建一个线程对象,如Thread thd = new Thread();
就绪:创建了线程 对象后。调用了线程的start()方法(注意:此时的线程只是进入了线程队列,等待获取CPU服务,具备了运行的条件,但并不一定已经开始运行了)
运行:处于就绪状态的线程,一旦获取了CPU资源,便进入到运行状态,开始执行run()方法里面的逻辑
终止:线程的run()方法执行完毕,或者线程调用了stop()方法(这个方法几乎被淘汰),线程便进入终止状态。
阻塞: 一个正在执行的线程在某些情况下,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法。
线程的守护神–守护线程
Java的线程有两类
用户线程:运行在前台,执行具体的任务
程序的主线程、连接网络的子线程都是用户线程
守护线程:运行在后台。为其他的前台线程服务
特点:一旦所有用户线程都结束运行,守护线程就会随JVM一起结束工作。
应用:数据库连接池中的监测线程
JVM虚拟机启动后的监测线程
最常见的守护线程:垃圾回收线程
如何设置守护线程
可以通过调用Thread类的setDaemon(true)方法来设置当前的线程为守护线程。
注意事项
- setDaemon(true) 必须在start()方法之前调用,否则会抛出IllegalThreadStateException异常
- 在守护线程中产生的新线程也是守护线程
- 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑
package com.mooc.thread;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Scanner;
class DeamonThread implements Runnable{
@Override
public void run() {
System.out.println("进入守护线程"+Thread.currentThread().getName());
try {
writeToFile();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("退出守护线程"+Thread.currentThread().getName());
}
public void writeToFile() throws Exception{
File filename = new File("d:"+File.separator+"test.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());
DeamonThread deamonThread = new DeamonThread();
Thread thread = new Thread(deamonThread);
thread.setDaemon(true);
thread.start();
Scanner scanner = new Scanner(System.in);
scanner.next();
System.out.println("退出主线程"+Thread.currentThread().getName());
}
}
使用jstack 生成线程快照
jstack 作用:生成JVM当前时刻线程的快照(threaddump,即当前进程中所有线程的信息)
目的:帮助定位程序中问题出现的原因,如长时间停顿、CPU占用率过高等。
补充:
- 程序中的同一资源指的是同一个Runable对象
- 安全的买票程序中需要加入同步(Synchronized)
上一篇: java面试.1
下一篇: java面试|精选基础(1)
推荐阅读
-
JAVA多线程Thread和Runnable的实现
-
Java多线程同步工具类之Semaphore
-
Java多线程系列--“JUC锁”06之 Condition条件
-
Java线程Thread的状态解析以及状态转换分析 多线程中篇(七)
-
Java中实现多线程继承Thread类与实现Runnable接口的区别
-
Java多线程Part3:线程(Thread)的状态和等待唤醒机制
-
java多线程学习之从正常到自残
-
用 JMX 作简单之 Java VM 监视(Part 1) JavaSUNJDK多线程J2SE
-
Java多线程并发编程中并发容器第二篇之List的并发类讲解
-
Java之多线程之线程池之线程重复使用