欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java多线程--细说多线程之Thread & Runnable

程序员文章站 2022-05-05 22:48:20
...

细说多线程之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 模拟买票场景
Java多线程--细说多线程之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();
    }
}

线程的生命周期

Java多线程--细说多线程之Thread & Runnable
创建: 新建一个线程对象,如Thread thd = new Thread();
就绪:创建了线程 对象后。调用了线程的start()方法(注意:此时的线程只是进入了线程队列,等待获取CPU服务,具备了运行的条件,但并不一定已经开始运行了)
运行:处于就绪状态的线程,一旦获取了CPU资源,便进入到运行状态,开始执行run()方法里面的逻辑
终止:线程的run()方法执行完毕,或者线程调用了stop()方法(这个方法几乎被淘汰),线程便进入终止状态。
阻塞: 一个正在执行的线程在某些情况下,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法。

线程的守护神–守护线程

Java的线程有两类

用户线程:运行在前台,执行具体的任务
程序的主线程、连接网络的子线程都是用户线程
守护线程:运行在后台。为其他的前台线程服务
特点:一旦所有用户线程都结束运行,守护线程就会随JVM一起结束工作。

应用:数据库连接池中的监测线程
JVM虚拟机启动后的监测线程
最常见的守护线程:垃圾回收线程

如何设置守护线程

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

注意事项
  • setDaemon(true) 必须在start()方法之前调用,否则会抛出IllegalThreadStateException异常
  • 在守护线程中产生的新线程也是守护线程
  • 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑

Java多线程--细说多线程之Thread & Runnable

    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占用率过高等。
Java多线程--细说多线程之Thread & Runnable
补充:

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