Java中多线程的完整指南

98 阅读6分钟

通常,在一个标准的Java程序中,整个程序作为一个 "线程 "运行。 这意味着,如果程序的某一部分需要某种I/O资源,例如,要进一步进行,而如果该I/O资源目前不可用,整个程序将处于等待状态,也不会执行,直到并且除非该需求的特定资源被释放。Java中的多线程是一个同时实现多个线程的过程。

多任务是指多个进程共享共同的处理资源,如CPU。 多线程将多任务的概念扩展到应用程序中,你可以将单个应用程序中的具体操作细分为各个线程。

每个线程都可以在系统内并行运行。这是因为操作系统将处理时间在应用程序和应用程序中的每个线程之间进行划分。

多线程使你能够编写,因此在同一个程序中,多个活动可以并发进行。

正如人们可以理解的那样,这在程序的执行速度方面提出了一个问题。为了解决这个问题,多线程的概念应运而生,程序中有多个线程可以并行运行。

什么是Java中的线程?

Java中的线程是轻量级的子进程,是最小的处理单元。多进程和多线程都是用来实现多任务的。 一个操作系统内可以有多个进程,而一个进程可以有多个线程。

多任务处理

多任务是指同时执行多个任务的过程。我们使用多任务来利用CPU。多任务可以通过两种方式实现。

  1. 基于进程的多任务处理(多进程处理)
  2. 基于线程的多任务处理(多线程)

1)基于进程的多任务处理(多处理)

  1. 每个进程在内存中都有一个地址。换句话说,每个进程分配一个单独的内存区域。
  2. 一个进程是重量级的。
  3. 进程之间的通信成本很高。
  4. 从一个进程切换到另一个进程需要一些时间来保存和加载寄存器、内存地图、更新列表等。

2)基于线程的多任务处理(多线程)

  1. 线程共享相同的地址空间。
  2. 一个线程是轻量级的。
  3. 线程之间的通信成本很低。

Java多线程

Java是多线程编程语言,这意味着我们可以用Java开发一个多线程程序。

一个多线程程序包含两个或多个可以并发运行的部分。每个部分可以同时处理不同的任务,使可用资源得到最佳利用,特别是当你的计算机有多个CPU时。

一个线程的生命周期

Life Cycle of a Thread

如上图所示,一个线程是在一个进程内执行的。线程之间有上下文切换。操作系统内可以有多个进程,一个进程可以有多个线程。

以下是生命周期的几个阶段

  • --一个新的线程在新的状态下开始它的生命周期。它一直处于这种状态,直到有程序启动该线程。它也被称为出生线程
  • 可运行- 在一个新诞生的线程被启动后,该线程成为可运行的。处于这种状态的线程被认为是在执行其任务。
  • 等待- 有时,当一个线程在等待另一个线程执行任务时,线程会过渡到等待状态。只有当另一个线程发出信号让等待中的线程继续执行时,线程才会过渡回可运行状态。
  • 定时等待- 一个可运行的线程可以在指定的时间间隔内进入定时等待状态。当这个时间间隔过后,或者当它所等待的事件发生时,处于这种状态的线程会转换回可运行状态。
  • Terminated (Dead)- 一个可运行线程在完成其任务或终止时进入关闭状态。

主线程

当一个Java程序启动时,有一个线程开始运行 - 主线程。

主线程是必不可少的,因为它是创建其他子线程的线程,而且主线程通常是最后完成执行的,因为它通常执行各种关闭功能。

主线程是在程序启动时自动创建的,它可以用一个 Thread 对象来控制 ,如下例所示:

// MainThreadDemo.java

class MainThreadDemo{
	public static void main(String [] args){
		Thread t = Thread.currentThread();
		System.out.println("Main thread: " + t); 
		
                //now changing the name of the thread
		t.setName("New Name");
		System.out.println("Name changed to: " + t);
	}
}

OUTPUT

Java Multithreading Tutorial With Example

在这里,第一条println语句打印出线程的名称为 "main",它的优先级为 "5",属于一个名为 "main "的线程组。

使用 setName() 方法,该线程的名称被改为 "新名称"。

线程优先级

每个线程都有一个线程优先级。如果一个线程目前正在CPU中执行,而一个优先级更高的线程随后要求使用CPU,那么优先级较低的线程将被抢占,并发生上下文切换,将CPU给优先级较高的线程。

还有一种方法,线程可以放弃控制权。它是通过自愿屈服、睡眠或在待定的I/O上阻塞。

创建一个线程

在Java中,有两种方法可以创建一个线程:

  1. 通过实现 Runnable 接口
  2. 通过扩展 线程

实现Runnable线程

请看下面SecondThread.java文件的代码。

// SecondThread.java

class SecondThread implements Runnable{
	Thread t;

	SecondThread(){
		t=new Thread(this, "Second Thread");
		System.out.println("Second Thread: " + t);
		t.start();

	}

	public void run(){
		try{
			for(int i=5; i>0; i--){
				System.out.println("Second Thread: " + i);
				Thread.sleep(1000);
			}
		}catch(InterruptedException e){
			System.out.println("Second interrupted.");
		}
		System.out.println("Exiting Second Thread");
	}
}

class RunnableDemo{
	public static void main(String [] args){
		new SecondThread();

		try{
			for(int i=5; i>0; i--){
				System.out.println("Main Thread: " + i);
				Thread.sleep(2000);
			}
		}catch(InterruptedException e){
			System.out.println("Main interrupted.");
		}
			
		
		System.out.println("Main thread exiting.");
	}
}

输出

Implementing Runnable Thread

新的线程是通过实例化 SecondThread 类 而创建的 。然后,在 SecondThread类 的构造函数中 , 创建了 一个 关于创建实例 的Thread 类的实例(因此,使用了 "this")。

start() 方法调用 run() 方法,它持有新线程的实际内容。当 run() 方法返回时,第二个线程完成了执行 。

Thread.sleep(double milliseconds**)**方法 自愿让线程在以毫秒为参数输入的时间内睡眠。

这个方法抛出了 InterruptedException, 它没有被处理;因此,每次调用这个方法时都必须使用try-catch机制。

从输出中可以看出,第二线程和主线程同时执行,第二线程首先完成,因为它得到的睡眠时间较少。 执行的顺序可能会因时而异。

扩展线程

请看下面的ExtendingThread.java文件的例子:

// ExtendingThreadDemo.java

class SecondThread implements Runnable{
    Thread t;

    SecondThread(){
        t=new Thread(this, "Second Thread");
        System.out.println("Second Thread: " + t);
        t.start();

    }

    public void run(){
        try{
            for(int i=5; i>0; i--){
                System.out.println("Second Thread: " + i);
                Thread.sleep(1000);
            }
        }catch(InterruptedException e){
            System.out.println("Second interrupted.");
        }
        System.out.println("Exiting Second Thread");
    }
}

class RunnableDemo{
    public static void main(String [] args){
        new SecondThread();

        try{
            for(int i=5; i>0; i--){
                System.out.println("Main Thread: " + i);
                Thread.sleep(2000);
            }
        }catch(InterruptedException e){
            System.out.println("Main interrupted.");
        }
            
        
        System.out.println("Main thread exiting.");
    }
}

输出

Extending Thread in Java

这里, SecondThread 类扩展了 Thread 类,它被实例化以创建一个新的线程。在 SecondThread的构造函数中 , super() 被用来将第二个线程的名字传递给 Thread的构造函数 。

程序的其余部分像实现 Runnable的例子一样执行 。