Java基础教程——多线程:创建线程

多线程

进程

每一个应用程序在运行时,都会产生至少一个进程(process)。
进程是操作系统进行“资源分配和调度”的独立单位。

Windows系统的“任务管理器”可以查看系统的进程,通过Ctrl+Shift+Esc组合键可以调出“任务管理器”。

进程具有三个特征:

  • 独立性:进程拥有自己独立的资源,有私有的地址空间。
  • 动态性:程序是静态的指令集合,而进程是活动的指令集合,进程有其生命周期。
  • 并发性:多个进程可以在同一个处理器上并发执行,互不影响。虽然同一时刻只能有一个进程执行,但是多个进程被快速轮换执行(时间片轮换算法),宏观上有同时执行的效果(同一时间段内执行)。

现代的操作系统都支持多进程并发执行。

线程

? 线程(Thread)是执行特定任务的最小单位,是进程的执行单元,也被称为轻量级进程(Lightweight Process)。
? 一个进程至少有一个线程,也可以有多个线程。所谓多线程就是指同一个进程可以同时并发处理多个任务。

? 线程没有系统资源,多个线程共享其所属进程的系统资源,因此多线程运行时,需要处理好资源同步问题。

总结:

  • 操作系统可以同时执行多个任务,每个任务是一个进程;
  • 进程可以同时执行多个任务,每个任务是一个线程。

多线程的优点

多线程就是下图这种感觉:

对计算机来说,多线程可以提高CPU的利用率。

例如:网络的数据传输速率远低于计算机的处理能力,如果是单线程程序,在下载网络资源时,CPU需要花费大量的空闲时间来等待,而多线程能够利用这些空闲时间完成其他任务。

具体来说:
|--一个浏览器可以在下载的同时打开其他网页;
|--Web服务器可以同时相应多个用户请求;
|--QQ发送文件的时候还能跟其他人继续聊天……

当前线程

Thread.currentThread():获取当前线程
.getName():获取线程名称

public class TestMainThread {
    public static void main(String[] args) {
        // 获得当前运行的线程
        Thread tMain = Thread.currentThread();
        // 线程名称,优先级,线程组
        System.out.println("当前运行的线程是:" + tMain);
        System.out.println("线程名称:" + tMain.getName());
    }
}

当前运行的线程是:Thread[main,5,main]
线程名称:main


创建线程

方法1:继承Thread类

Thread类代表线程,所有的线程对象都是Thread类或其子类的对象。
创建线程的步骤:

  1. 继承Thread类
  2. 重写run()方法(线程执行体)
  3. 创建Thread类的实例对象
  4. 调用start()方法启动线程

可以使用setName(…)为线程设置名字,用getName()获取。
默认情况下,主线程名为main,子线程名为Thread-0、Thread-1等。

public class _11NewThread {
    public static void main(String[] args) {
        Thread t1 = new MyT();
        t1.setName("1");
        t1.start();
        Thread t2 = new MyT();
        t2.setName("   2");
        t2.start();
    }
}
class MyT extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程:" + super.getName());
        }
    }
}

方法2:实现Runnable接口

Runnable是个接口,实现Runnable接口的类还可以继承其它类,这是比方法1优越的地方。

  1. 实现Runable接口
  2. 重写run()方法
  3. 创建Runable对象
  4. 创建Thread对象
  5. 调用thread.start()方法,启动线程

实际创建的对象还是Thread实例。

public class _12NewThreadRunnable {
    public static void main(String[] args) {
        MyR r1 = new MyR("A");
        Thread t1 = new Thread(r1);
        t1.setName("XXX");
        t1.start();
        MyR r2 = new MyR("  B");
        Thread t2 = new Thread(r2);
        t2.start();
    }
}
class MyR implements Runnable {
    public MyR(String s) {
        this.s = s;
    }
    private String s;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程:" + s);
        }
    }
}

*方法3:实现Callable接口

使用Callable和FutureTask创建线程——可以获取线程的返回值。这种做法了解即可。

  1. 实现Callable接口的线程,需重写call()方法,有返回值
  2. 需要FutureTask类包装一下Callablle,该类实现了Runable接口
  3. 以Future对象为参数创建线程,调用start()方法启动
  4. 以Futrue对象.get()接收返回值
package ahjava.p06thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
// 实现Callable接口的线程,需重写call()方法,有返回值
// 需要FutureTask类包装一下Callablle,该类实现了Runable接口
// 以Future对象为参数创建线程,调用start()方法启动
// 以Futrue对象.get()接收返回值
public class _13NewThreadCallable {
    public static void main(String[] args) {
        MyCallable _Callable = new MyCallable("A");
        FutureTask<String> _FutureTask = new FutureTask<>(_Callable);
        Thread _Thread = new Thread(_FutureTask);
        _Thread.start();
        try {
            // 模拟等待,此过程可看到子线程在执行
            Thread.sleep(3000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        // 接受子线程的返回值
        try {
            String s = _FutureTask.get();
            System.out.println("子线程的返回值 = " + s);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
class MyCallable implements Callable<String> {
    public MyCallable(String s) {
        this.s = s;
    }
    private String s;
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程:" + s);
        }
        return "我是子线程,已经执行完毕";
    }
}

start()和run()

调用start()是开启新线程,调用run()是普通方法调用。

方法调用是入栈操作,调用完毕则出栈。
如果直接调用run()方法,则是在原线程中进行方法调用,用的还是之原线程的栈。
调用start()才是开启一个新线程,会创建新的栈。


线程的优先级

最高 10 Thread.MAX_PRIORITY
默认 5 Thread.NORM_PRIORITY
最低 1 Thread.MIN_PRIORITY

演示示例:

Thread(Runnable target, String name):为线程直接命名
Thread.currentThread():获取当前线程

public class Priority线程优先级 {
    // *可能不太容易看出来
    public static void main(String[] args) {
        MyRu r1 = new MyRu();
        MyRu r2 = new MyRu();
        MyRu r3 = new MyRu();
        Thread t1 = new Thread(r1);
        // 创建时命名
        Thread t2 = new Thread(r2, "   B");
        Thread t3 = new Thread(r3);
        t1.setName("A");
        t3.setName("       C");
        t1.setPriority(Thread.MAX_PRIORITY);
        t3.setPriority(Thread.MIN_PRIORITY);
        t1.start();
        t2.start();
        t3.start();
    }
}
class MyRu implements Runnable {
    @Override
    public void run() {
        String sName = Thread.currentThread().getName();
        int sPri = Thread.currentThread().getPriority();
        for (int i = 0; i < 100; i++) {
            System.out.println("线程:" + sName + " 优先级:" + sPri);
        }
    }
}

多线程的运行结果是不确定的,运行结果每次不同,可能有些时候看不出不同优先级的区别。

原文地址:https://www.cnblogs.com/tigerlion/p/11179234.html

时间: 2024-08-01 03:14:26

Java基础教程——多线程:创建线程的相关文章

Java基础学习——多线程之线程池

1.线程池介绍     线程池是一种线程使用模式.线程由于具有空闲(eg:等待返回值)和繁忙这种不同状态,当数量过多时其创建.销毁.调度等都会带来开销.线程池维护了多个线程,当分配可并发执行的任务时,它负责调度线程执行工作,执行完毕后线程不关闭而是返回线程池,可以执行后续其他任务.举例来说,外卖餐厅对每个订单分配一个临时工,完成订单立刻辞退,成本和管理开销巨大,而且如果取餐点的空间有限,大量的人挤满在那反而会影响工作效率,因此选择签下固定数量的外卖小哥让他们不断往返于目的地和店家之间配送. 通俗

Java基础教程:多线程基础(2)——线程间的通信

Java基础教程:多线程基础(2)--线程间的通信 使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时还会使程序员对各线程任务在处理的过程中进行有效的把控与监督. 线程间的通信 思维导图 等待中 等待/通知机制 不使用等待/通知机制 我们可以使用使用sleep()与 whle(true) 死循环来实现多个线程间的通信. 虽然两个线程实现了通信,但是线程B必须不断的通过while语句轮训机制来检测某一个条件,这样会浪费CPU资源. 如果轮询间隔较小,更浪费时间间隔.如果轮训

Java基础教程:HashTable与HashMap比较

Java基础教程:HashTable与HashMap比较 1.  关于HashMap的一些说法: a)  HashMap实际上是一个"链表散列"的数据结构,即数组和链表的结合体.HashMap的底层结构是一个数组,数组中的每一项是一条链表. b)  HashMap的实例有俩个参数影响其性能: "初始容量" 和 装填因子. c)  HashMap实现不同步,线程不安全.  HashTable线程安全 d)  HashMap中的key-value都是存储在Entry中的

Java基础教程:内部类

Java基础教程:内部类 内部类 内部类,是指在一个类的内部定义的类.就像下面这样: public class EnclosingClass {   . . .   public class NestedClass {   . . .     } } 内部类拥有访问外部类所有元素(包括private.static)的访问权.当某个外部类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用.然后,在你访问此外围类的成员时,就是用那个引用来选择外围类的成员. 内部类

Java基础教程:面向对象编程[2]

Java基础教程:面向对象编程[2] 三大特性 封装 封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装.隐藏起来的方法.封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问. 使用封装我们可以对成员变量进行更精确的控制,同时隐藏信息,实现细节等. 方法: public class Person{ private String name; private int age; ? public int getAge(){ return age;

Java基础教程:JDBC编程

Java基础教程:JDBC编程 快速开始 什么是JDBC JDBC 指 Java 数据库连接,是一种标准Java应用编程接口( JAVA API),用来连接 Java 编程语言和广泛的数据库. JDBC API 库包含下面提到的每个任务,都是与数据库相关的常用用法. 制作到数据库的连接. 创建 SQL 或 MySQL 语句. 执行 SQL 或 MySQL 查询数据库. 查看和修改所产生的记录. 从根本上来说,JDBC 是一种规范,它提供了一套完整的接口,允许便携式访问到底层数据库,因此可以用 J

Java基础教程(25)--I/O流

??I/O流表示输入源或输出目标.流可以表示许多不同类型的源和目标,例如磁盘文件.设备.其他程序等. ??流支持许多不同类型的数据,包括字节.原始数据类型.字符和对象等.有些流只传递数据; 有些流则可以操纵和转换数据. ??无论各种流的内部是如何工作的,所有流都提供相同的简单模型:流是一系列数据.程序使用输入流从源头获取数据,一次一项: ??程序使用输出流将数据写入目的地,一次一项: ??在本文中,我们会看到流可以处理各种各样的数据,无论是基本数据还是复杂对象.先来几张IO流的全家福: ??In

Java基础教程 - 组合

1. 什么是组合? 如果一个类的对象和另一个类满足"has-a"关系的话,我们就可以在一个类中,把另一个类的对象作为其对象成员. 什么是"has-a"关系,举个例子:现在有一个类LapTop.class,还有一个是Moniter.class.好显然,Laptop "has-a" Moniter,也就是说,他们是满足"has-a"关系的.这时候,我们就可以把Moniter作为Laptop的一个数据成员. class Laptop

OpenVAS漏洞扫描基础教程之创建用户组与创建角色

OpenVAS漏洞扫描基础教程之创建用户组与创建角色 OpenVAS创建用户组 用户组就是指许多个用户的组合.在网络中,各个访问网络的用户的权限可能各不相同.所以,可以通过将具体相同权限的用户划为一组.这样就不需要单独为某个用户设置权限了,只需要设置组的权限即可.下面将介绍创建用户组的方法.具体操作步骤如下所示: (1)在OpenVAS服务的主界面依次单击Administration|Groups命令,即可打开用户组界面,如图1.16所示.   图1.16  组界面 (2)从该界面可以看到,目前