黑马程序员_线程基础

本文详细介绍了Java中创建线程的两种主要方法:继承Thread类和实现Runnable接口。通过具体的代码示例展示了如何使用这两种方法,并解释了它们之间的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 一、概述  

    由于Java是纯面向对象语言,因此,Java的线程模型也是面向对象的。Java通过Thread类将线程所必须的功能都封装了起来。要想建立一个线程,必须要有一个线程执行函数,这个线程执行函数对应Thread类的run方法。Thread类还有一个start方法,这个方法负责建立线程。当调用start方法后,如果线程建立成功,并自动调用Thread类的run方法。因此,任何继承Thread的Java类都可以通过Thread类的start方法来建立线程。如果想运行自己的线程执行函数,那就要覆盖Thread类的run方法。注意:Java中的多线程是一种抢占机制而不是分时机制。抢占机制指的是有多个线程处于可运行状态,但是只允许一个线程在运行,他们通过竞争的方式抢占CPU。

在Java的线程模型中除了Thread类,还有一个标识某个Java类是否可作为线程类的接口Runnable,这个接口只有一个抽象方法run,也就是Java线程模型的线程执行函数。因此,一个线程类的唯一标准就是这个类是否实现了Runnable接口的run方法,也就是说,拥有线程执行函数的类就是线程类。

从上面可以看出,在Java中建立线程有两种方法,一种是继承Thread类,另一种是实现Runnable接口,并通过Thread和实现Runnable的类来建立线程,其实这两种方法从本质上说是一种方法,即都是通过Thread类来建立线程,并运行run方法的。但它们的大区别是通过继承Thread类来建立线程,虽然在实现起来更容易,但由于Java不支持多继承,因此,这个线程类如果继承了Thread,就不能再继承其他的类了,因此,Java线程模型提供了通过实现Runnable接口的方法来建立线程,这样线程类可以在必要的时候继承和业务有关的类,而不是Thread类。

二、介绍

从java的API开始,因为Runnable是顶层接口,所以先说一下它。API中是这样说明的:Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。此外,Runnable 为非 Thread 子类的类提供了一种激活方式。通过实例化某个 Thread 实例并将自身作为运行目标,就可以运行实现 Runnable 的类而无需创建 Thread 的子类。大多数情况下,如果只想重写 run() 方法,而不重写其他 Thread 方法,那么应使用 Runnable 接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。 

通过说明我们知道,要想使用Runnable来创建线程,实现Runnable接口的类必须使用Thread类的实例才能创建线程。通过Runnable接口创建线程分为三步:

1. 将实现Runnable接口的类实例化。

2. 建立一个Thread对象,并将第一步实例化后的对象作为参数传入Thread类的构造方法。

3. 最后通过Thread类的start方法建立线程。

下面的代码演示了如何使用Runnable接口来创建线程:

[java]  view plain copy
  1. package com.debug.java;  
  2.   
  3. public class RunnableDemo implements Runnable {  
  4.   
  5.     /** 
  6.  
  7.      * @category 第一步:将实现Runnable接口的类实例化。 
  8.  
  9.      */  
  10.   
  11.     public static void main(String[] args) {  
  12.   
  13.         RunnableDemo r = new RunnableDemo();// 第二步:实例化RunnableDemo,并将其作为Thread的参数  
  14.   
  15.         Thread thread1 = new Thread(r);  
  16.   
  17.         Thread thread2 = new Thread(r, "thread2");  
  18.   
  19.         thread1.start();// 第三步:开始线程  
  20.   
  21.         thread2.start();  
  22.   
  23.     }  
  24.   
  25.     @Override  
  26.   
  27.     public void run() {  
  28.   
  29.         System.out.println(Thread.currentThread().getName());  
  30.   
  31.     }  
  32.   
  33. }  

输出:Thread-0

            thread2

介绍完Runnable接口,再来看一下Thread类,它继承自Runnable接口。 Thread类的构造方法被重载了八次,构造方法如下:
  1. public Thread( );  
  2. public Thread(Runnable target);  
  3. public Thread(String name);  
  4. public Thread(Runnable target, String name);  
  5. public Thread(ThreadGroup group, Runnable target);  
  6. public Thread(ThreadGroup group, String name);  
  7. public Thread(ThreadGroup group, Runnable target, String name);  
  8. public Thread(ThreadGroup group, Runnable target, String name, long stackSize); 

Runnable target:

实现了Runnable接口的类的实例。要注意的是Thread类也实现了Runnable接口,因此,从Thread类继承的类的实例也可以作为target传入这个构造方法。

String name:

线程的名子。这个名子可以在建立Thread实例后通过Thread类的setName方法设置。如果不设置线程的名子,线程就使用默认的线程名:Thread-N,N是线程建立的顺序,是一个不重复的正整数。

ThreadGroup group:

当前建立的线程所属的线程组。如果不指定线程组,所有的线程都被加到一个默认的线程组中。

long stackSize:

线程栈的大小,这个值一般是CPU页面的整数倍。如x86的页面大小是4KB。在x86平台下,默认的线程栈大小是12KB。

一个普通的Java类只要从Thread类继承,就可以成为一个线程类。并可通过Thread类的start方法来执行线程代码。虽然Thread类的子类可以直接实例化,但在子类中必须要覆盖Thread类的run方法才能真正运行线程的代码

它创建线程的方法是:
1、将类声明为 Thread 的子类
2、该子类应重写 Thread 类的 run 方法
3、接下来可以分配并启动该子类的实例
下面的代码演示了如何使用Runnable接口来创建线程:
[java]  view plain copy
  1. package com.debug.java;  
  2. public class ThreadDemo extends Thread{  
  3.     /* 
  4.      * 重写run方法 
  5.      */  
  6.     @Override  
  7.     public void run() {  
  8.         super.run();  
  9.         System.out.println(Thread.currentThread().getName());  
  10.     }  
  11.     /** 
  12.      * @param args 
  13.      */  
  14.     public static void main(String[] args) {  
  15.         System.out.println("main线程:"+Thread.currentThread().getName());   
  16.         ThreadDemo thread1=new ThreadDemo();  
  17.         ThreadDemo thread2=new ThreadDemo();  
  18.         thread1.start();  
  19.         thread2.start();  
  20.     }  
  21. }  
输出:main线程:main
      Thread-0
      Thread-1     由于在建立线程时并未指定线程名,因此,所输出的线程名是系统的默认值,也就是Thread-n的形式
注意:任何一个Java程序都必须有一个主线程。一般这个主线程的名子为main。只有在程序中建立另外的线程,才能算是真正的多线程程序。也就是说,多线程程序必须拥有一个以上的线程。 Thread类的start方法不能多次调用,如不能调用两次thread1.start()方法。否则会抛出一个IllegalThreadStateException异常。
Thread类有一个重载构造方法可以设置线程名。除了使用构造方法在建立线程时设置线程名,还可以使用Thread类的setName方法修改线程名。要想通过Thread类的构造方法来设置线程名,必须在Thread的子类中使用Thread类的public Thread(String name)构造方法,因此,必须在Thread的子类中也添加一个用于传入线程名的构造方法。下面的代码给出了一个设置线程名的例子:
[java]  view plain copy
  1. package com.debug.java;  
  2.   
  3. public class ThreadDemo extends Thread {  
  4.   
  5.     private String str;  
  6.   
  7.     public ThreadDemo(String str) {  
  8.   
  9.         this.str = str;  
  10.     }  
  11.   
  12.     public ThreadDemo(String str, String threadName) {  
  13.         super(threadName);  
  14.         this.str = str;  
  15.     }  
  16.   
  17.     /* 
  18.      * 重写run方法 
  19.      */  
  20.     @Override  
  21.     public void run() {  
  22.         super.run();  
  23.         System.out.println(Thread.currentThread().getName());  
  24.     }  
  25.   
  26.     /** 
  27.      * @param args 
  28.      */  
  29.     public static void main(String[] args) {  
  30.         System.out.println("main线程:" + Thread.currentThread().getName());  
  31.         ThreadDemo thread1 = new ThreadDemo("thread1");  
  32.         thread1.setName("thread1");  
  33.         ThreadDemo thread2 = new ThreadDemo("thread2""thread2");  
  34.         ThreadDemo thread3 = new ThreadDemo("thread2""thread2");  
  35.         thread1.start();  
  36.         thread2.start();  
  37.         thread3.start();  
  38.     }  
  39.   
  40. }  
输出:main线程:main
thread1
thread2
thread2
注意:在调用start方法前后都可以使用setName设置线程名,但在调用start方法后使用setName修改线程名,会产生不确定性,也就是说可能在run方法执行完后才会执行setName。如果在run方法中要使用线程名,就会出现虽然调用了setName方法,但线程名却未修改的现象。

在C#多线程编程中,线程的创建和管理是通过`System.Threading`命空间中的`Thread`类来实现的。`Thread`类允许开发者创建和控制线程设置线程的优先级,并启动线程执行特定的任务。以下是一些关键概念和示例代码,帮助理解C#多线程编程的基础。 ### 线程的创建与启动 要创建一个新的线程,首先需要定义一个没有参数且返回类型为`void`的方法,这个方法将作为线程的入口点。然后,使用`ThreadStart`委托将这个方法传递给`Thread`类的构造函数,从而创建一个线程实例。最后,调用`Start`方法来启动线程。 ```csharp public static void Thread1() { for (int i = 1; i < 1000; i++) { // 模拟复杂运算 DoSomething(); Console.Write("1"); } } public static void Thread2() { for (int i = 0; i < 1000; i++) { // 模拟复杂运算 DoSomething(); Console.Write("2"); } } public static void DoSomething() { for (int j = 0; j < 10000000; j++) { int a = 15; a = a * a * a * a; } } ``` ### 设置线程优先级 每个线程都有一个优先级,它决定了操作系统分配给该线程的CPU时间片的相对长度。可以通过设置`Priority`属性来调整线程的优先级,其值可以是`ThreadPriority`枚举中的任何一个值,如`Normal`、`Lowest`、`Highest`等。 ```csharp Thread t1 = new Thread(new ThreadStart(Thread1)); Thread t2 = new Thread(new ThreadStart(Thread2)); t1.Priority = ThreadPriority.BelowNormal; t2.Priority = ThreadPriority.Lowest; t1.Start(); t2.Start(); ``` ### 执行带参数的方法 如果需要在线程启动时传递参数给线程方法,可以使用`ParameterizedThreadStart`委托。这种方法接受一个`object`类型的参数,因此可以在调用`Start`方法时传递任何对象。 ```csharp public static void ShowName(object name) { Console.WriteLine(name.ToString()); } private void StartThreadWithParameter() { Thread threadShowName = new Thread(ShowName); threadShowName.IsBackground = true; threadShowName.Start("刘德华"); } ``` ### 线程同步 当多个线程访问共享资源时,可能会出现数据不一致的问题。为了确保数据的一致性和完整性,可以使用`lock`语句或者`Monitor`类的方法来进行线程同步。 ```csharp private static object lockObject = new object(); public static void SafeMethod() { lock (lockObject) { // 安全地访问共享资源 } } ``` 以上内容涵盖了C#多线程编程的一些基础方面,包括线程的创建、优先级设置、参数传递以及基本的线程同步技术。通过这些基础概念的学习,可以为进一步探索更高级的多线程编程技巧打下坚实的基础
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值