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占用率过高等。
在这里插入图片描述
补充:

  1. 程序中的同一资源指的是同一个Runable对象
  2. 安全的买票程序中需要加入同步(Synchronized)

猜你喜欢

转载自blog.csdn.net/u013785405/article/details/88182983