• 2.线程创建方式(3种)


    引自:https://www.runoob.com/java/java-multithreading.html

    引自:https://blog.csdn.net/boker_han/article/details/82918881#Runnable_39

    线程:指的是进程中一个个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程是jvm调度的最小单元,也叫做轻量级进程,进程是由线程组成,线程拥有私有的程序计数器以及栈,并且能够访问堆中的共享资源。多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

    进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

    为什么需要多线程:

      1)随着cpu核心数的增加,计算机硬件的并行计算能力得到提升,而同一个时刻一个线程只能运行在一个cpu上,那么计算机的资源被浪费了,所以需要使用多线程。

      2)也是为了提高系统的响应速度,如果系统只有一个线程可以执行,那么当不同用户有不同的请求时,由于上一个请求没处理完,那么其他的用户必定需要在一个队列中等待,大大降低响应速度,所以需要多线程。

    一个线程的生命周期

    • 新建状态:

      使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

    • 就绪状态:

      当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

    • 运行状态:

      如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

    • 阻塞状态:

      如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

      • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

      • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

      • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

    • 死亡状态:

          一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态

     

    线程的优先级

    每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

    Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

    默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。

    具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。

    线程的三种创建方式

    1.继承Thread类

       1)创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。

       2)继承类必须重写 run() 方法,该方法是新线程的入口点。

       3)也必须调用 start() 方法才能执行。

    该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。

     例1:

    class ThreadDemo extends Thread {
       private Thread t;
       private String threadName;
       
       ThreadDemo( String name) {
          threadName = name;
          System.out.println("Creating " +  threadName );
       }
       
       public void run() {
          System.out.println("Running " +  threadName );
          try {
             for(int i = 4; i > 0; i--) {
                System.out.println("Thread: " + threadName + ", " + i);
                // 让线程睡眠一会
                Thread.sleep(50);
             }
          }catch (InterruptedException e) {
             System.out.println("Thread " +  threadName + " interrupted.");
          }
          System.out.println("Thread " +  threadName + " exiting.");
       }   
      public void start () {
          System.out.println("Starting " +  threadName );
          if (t == null) {
             t = new Thread (this, threadName);
             t.start ();
          }
       }
    }
     
    public class TestThread {
     
       public static void main(String args[]) {
          ThreadDemo T1 = new ThreadDemo( "Thread-1");
          T1.start();
          
          ThreadDemo T2 = new ThreadDemo( "Thread-2");
          T2.start();
       }   
    }

      例2:

     1 class MyThread extends Thread {
     2     
     3     private int i = 0;
     4 
     5     @Override
     6     public void run() {
     7         for (i = 0; i < 100; i++) {
     8             System.out.println(Thread.currentThread().getName() + " " + i);
     9         }
    10     }
    11 }
    12 
    13 
    14 public class ThreadTest {
    15 
    16     public static void main(String[] args) {
    17         for (int i = 0; i < 100; i++) {
    18             System.out.println(Thread.currentThread().getName() + " " + i);
    19             if (i == 30) {
    20                 Thread myThread1 = new MyThread();     // 创建一个新的线程  myThread1  此线程进入新建状态
    21                 Thread myThread2 = new MyThread();     // 创建一个新的线程 myThread2 此线程进入新建状态
    22                 myThread1.start();                     // 调用start()方法使得线程进入就绪状态
    23                 myThread2.start();                     // 调用start()方法使得线程进入就绪状态
    24             }
    25         }
    26     }
    27 }

    2.实现Runnable接口

    优点:

    • 扩展性好,可以在此基础上继承其他类,实现其他必需的功能
    • 对于多线程共享资源的场景,具有天然的支持,适用于多线程处理一份资源的场景

    缺点:构造线程实例的过程相对繁琐一点

      例1:

     1 class RunnableDemo implements Runnable {
     2    private Thread t;
     3    private String threadName;
     4    
     5    RunnableDemo( String name) {
     6       threadName = name;
     7       System.out.println("Creating " +  threadName );
     8    }
     9    
    10    public void run() {
    11       System.out.println("Running " +  threadName );
    12       try {
    13          for(int i = 4; i > 0; i--) {
    14             System.out.println("Thread: " + threadName + ", " + i);
    15             // 让线程睡眠一会
    16             Thread.sleep(50);
    17          }
    18       }catch (InterruptedException e) {
    19          System.out.println("Thread " +  threadName + " interrupted.");
    20       }
    21       System.out.println("Thread " +  threadName + " exiting.");
    22    }
    23    
    24    public void start () {
    25       System.out.println("Starting " +  threadName );
    26       if (t == null) {
    27          t = new Thread (this, threadName);
    28          t.start ();
    29       }
    30    }
    31 }
    32  
    33 public class TestThread {
    34  
    35    public static void main(String args[]) {
    36       RunnableDemo R1 = new RunnableDemo( "Thread-1");
    37       R1.start();
    38       
    39       RunnableDemo R2 = new RunnableDemo( "Thread-2");
    40       R2.start();
    41    }   
    42 }

      例2:

     1 package com.thread;
     2 public class MyRunnable implements Runnable {
     3     //线程体
     4     @Override
     5     public void run() {
     6         System.out.println("Hello, I am the defined thread created by implements Runnable");
     7     }
     8     public static void main(String[] args){
     9         //线程的执行目标对象
    10         MyRunnable myRunnable = new MyRunnable();
    11         //实际的线程对象
    12         Thread thread = new Thread(myRunnable);
    13         //启动线程
    14         thread.start();
    15     }
    16 }

    3.实现Callable接口

    • 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
    • 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
    • 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
    • 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

    优点:

    • 扩展性好
    • 支持多线程处理同一份资源
    • 具备返回值以及可以抛出受检查异常

    缺点:

    • 相较于实现Runnable接口的方式,较为繁琐

       例1:

    public class CallableThreadTest implements Callable<Integer> {
        public static void main(String[] args)  
        {  
            CallableThreadTest ctt = new CallableThreadTest();  
            FutureTask<Integer> ft = new FutureTask<>(ctt);  
            for(int i = 0;i < 100;i++)  
            {  
                System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);  
                if(i==20)  
                {  
                    new Thread(ft,"有返回值的线程").start();  
                }  
            }  
            try  
            {  
                System.out.println("子线程的返回值:"+ft.get());  
            } catch (InterruptedException e)  
            {  
                e.printStackTrace();  
            } catch (ExecutionException e)  
            {  
                e.printStackTrace();  
            }  
      
        }
        @Override  
        public Integer call() throws Exception  
        {  
            int i = 0;  
            for(;i<100;i++)  
            {  
                System.out.println(Thread.currentThread().getName()+" "+i);  
            }  
            return i;  
        }  
    }

       例2:

    package com.thread;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "Hello, I am the defined thread created by implements Callable";
        }
        public static void main(String[] args){
            //线程执行目标
            MyCallable myCallable = new MyCallable();
            //包装线程执行目标,因为Thread的构造函数只能接受Runnable接口的实现类,而FutureTask类实现了Runnable接口
            FutureTask<String> futureTask = new FutureTask<>(myCallable);
            //传入线程执行目标,实例化线程对象
            Thread thread = new Thread(futureTask);
            //启动线程
            thread.start();
            String result = null;
            try {
                //获取线程执行结果
                result = futureTask.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            System.out.println(result);
        }
    }

    创建线程的三种方式的对比

    • 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
    • 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。

    在多线程编程时,需要了解以下几个概念:

    • 线程同步
    • 线程间通信
    • 线程死锁
    • 线程控制:挂起、停止和恢复

    多线程与高并发编程之基础知识:

    参看:https://blog.csdn.net/boker_han/article/details/79466524

  • 相关阅读:
    什么叫委托
    什么是继承
    什么叫多态
    委托的了解
    什么是数组
    工作记录之 oracle去重的三个方法
    实例分析J2ME网络编程的两种方法
    在无线J2ME设备上实现超文本传输协议
    java与C、C++进行通信的一些问题
    如何配置Wiindows live writer
  • 原文地址:https://www.cnblogs.com/aaronRhythm/p/10775493.html
Copyright © 2020-2023  润新知