一、简介

  在计算机的世界里,当我们谈论并发时,我们指的是一系列的任务同时运行于一个计算机中。这里说的同时运行,在计算机拥有多于一个处理器或者是一个多核处理器的时候才是真正的同时,在计算机只拥有单核处理器的时候,它指的是表面上的同时运行。

  所有的现代操作系统都允许并发任务的执行。在听歌和阅读网页上新闻的同时,你还能阅读电子邮件。我们可以说这种类型的并发是进程级别的并发。但在一个进程内部,我们也可以拥有多个同时运行的任务。那些运行在一个进程中的并发任务被称作线程。

  与并发相关的另一个概念是并行。它与并发之间存在着紧密的联系和区别。有人认为,在单核处理器中执行具有多个线程的进程就叫做并发,这个并发被认为是表面的并发。同时,在多核处理器或者多处理中执行具有多个线程的进程就叫做并行。其他人则认为,当一个进程的多个线程执行之间没有预定义好的顺序时就叫做并发,当使用多个线程去简化一个问题的求解,同时所有的线程按照一定的顺序执行时就叫做并行。

  本章呈现了一系列的代码秘诀,这些代码秘诀演示了如何使用 Java 7 API 去执行基本的线程操作。你可以从中学到在Java程序中如何创建和运行线程,如何控制线程的执行,如何分组线程并使用线程组的方式来操纵组内线程。

二、创建和运行一个线程

  在这个秘诀中,我们将学到如何在Java程序中去创建和运行一个线程。就像Java语言中的其他元素一样,线程也是以对象的形式存在。在Java中,创建线程有两种方式:

  (A) 继承 Thread 类,覆写 run() 方法

  (B) 创建一个新类,该类实现 Runnable 接口,该类的对象传给 Thread 类的对象

  在本秘诀中,我们通过一个创建和运行10个线程的简单程序来演示第二种方式.每个线程都是计算和打印输出1到10之间的乘法表。

public class Calculator implements Runnable{
    private int number;
    public Calculator(int number) {
        this.number = number;
    }
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.printf("%s: %d * %d = %d\n",
                    Thread.currentThread().getName(),
                    number, i, i*number);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            Calculator calculator = new Calculator(i);
            Thread thread = new Thread(calculator);
            thread.start();
        }
    }
}

  main()方法所在的执行线程是由JVM启动执行时创建的,一般我们称之为主线程。

  通过调用一个 Thread 对象的 start() 方法,我们创建了一个另外的执行线程。

  只有当程序的所有非后台线程结束后,该程序才算结束。但是要注意:一旦某一个线程执行了System.exit()指令,所有的线程都将终止执行,该程序也就结束了。

  创建一个 Thread 类的对象并没有创建一个新的执行线程。调用实现了 Runnable 接口的对象的 run() 方法也不会创建一个新线程。只有调用 Thread 对象的 start() 方法才创建新线程。

三、获取和设置线程信息

  Thread 类保存有一些能帮助我们识别一个线程的信息属性。通过这些属性,我们可以知道它的状态,控制它的优先级。这些属性是:

  (A) ID:每个线程的唯一标识符

  (B) Name:线程的名字

  (C) Priority:线程的优先级,1到10之间,1最低,不建议修改和利用此属性

  (D) Status:线程的状态,六种可能:新建、可运行、被阻塞、等待、等待时间、被终结

  在本秘诀中,我们创建拥有10个线程的程序,设置10个线程的名字和优先级,然后展示它们的状态知道它们结束,这些线程会计算一个数字的乘法表。

public class Calculator implements Runnable{
    private int number;
    public Calculator(int number) {
        this.number = number;
    }
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.printf("%s: %d * %d = %d\n",
                    Thread.currentThread().getName(),
                    number, i, i*number);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Thread threads[] = new Thread[10];
        Thread.State status[] = new Thread.State[10];

        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(new Calculator(i));
            if ((i%2) == 0) {
                threads[i].setPriority(Thread.MAX_PRIORITY);
            } else {
                threads[i].setPriority(Thread.MIN_PRIORITY);
            }
            threads[i].setName("Thread " +i);
        }

        try (FileWriter fw = new FileWriter("log.txt");
                PrintWriter pw = new PrintWriter(fw); ) {
            for (int i = 0; i < 10; i++) {
                pw.println("Main : Status of Thread " + i + " : " +
            threads[i].getState());
                status[i] = threads[i].getState();
            }

            for (int i = 0; i < 10; i++) {
                threads[i].start();
            }

            boolean finish = false;
            while (!finish) {
                for (int i = 0; i < 10; i++) {
                    if (threads[i].getState() != status[i]) {
                        writeThreadInfo(pw, threads[i], status[i]);
                        status[i] = threads[i].getState();
                    }
                }
                finish = true;
                for (int i = 0; i < 10; i++) {
                    finish = finish && (threads[i].getState() == State.TERMINATED);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void writeThreadInfo(PrintWriter pw,
            Thread thread, State state) {
        pw.printf("Main : Id %d - %s\n", thread.getId(), thread.getName());
        pw.printf("Main : Priority: %d\n", thread.getPriority());
        pw.printf("Main : Old State: %s\n", state);
        pw.printf("Main : New State: %s\n", thread.getState());
        pw.printf("Main : *******************************\n");
    }
}

  如果不指定线程名字,JVM会自动给它分配 Thread-XX 格式的线程名。线程的ID和Status是不能修改的。

  如果是想在实现了 Runnable 接口的类的对象中访问线程的信息,可以使用 Thread 类的静态方法 currentThread() 获取执行当前代码的 Thread 对象,并以此来访问线程信息。

  需要注意的是:setPriority() 方法可能抛出 IllegalArgumentException 溢出。

  重要:本系列翻译文档也会在本人的微信公众号(此山是我开)第一时间发布,欢迎大家关注。

  

Java 7 Concurrency Cookbook 翻译 第一章 线程管理之一的更多相关文章

  1. Java 7 Concurrency Cookbook 翻译 第一章 线程管理之六

    十一.处理线程组中的未控制异常 每种编程语言一个很重要的特性就是其所提供的用来处理程序中错误情况的机制.Java语言和其他的现代语言一样,是提供了异常机制来处理对象程序中的错误.Java提供了很多的类 ...

  2. Java 7 Concurrency Cookbook 翻译 第一章 线程管理之五

    九.使用线程本地变量 一个并发程序的最关键特征就是共享数据.这个特性在那些继承了 Thread 类或者 实现了 Runnable 接口的对象上显得更加重要. 如果你创建一个实现了 Runnable 接 ...

  3. Java 7 Concurrency Cookbook 翻译 第一章 线程管理之四

    七.创建和运行一个后台线程 Java中有一种特别的线程叫做 deamon(后台) 线程.这类线程具有非常低的权限,并且只有在同一个程序中没有其他的正常线程在运行时才会运行.注意:当一个程序中只剩下后台 ...

  4. Java 7 Concurrency Cookbook 翻译 第一章 线程管理之二

    三.中断一个线程 一个拥有多个线程的Java程序要结束,需要满足两个条件之一:一是所有的非后台线程都执行结束了:二是某个线程执行了 System.exit() 方法.当你想要终结一个运行中的Java程 ...

  5. Java 7 Concurrency Cookbook 翻译 第一章 线程管理之三

    五.睡眠和唤醒一个线程 有时,你会想要在一段特定的时间后再去中断线程的运行.举个例子,程序中的一个线程每一分钟检查一次传感器的状态,剩余的时间,线程应该处于空闲的状态.在这段空闲时间里,线程不会使用计 ...

  6. Java 7 Concurrency Cookbook 翻译 序言

    在日常的Java代码开发过程中,很难免地有对多线程的需求,掌握java多线程和并发的机制也是Java程序员写出更健壮和高效代码的基础.笔者找寻国内已出版的关于Java多线程和并发的的中文书籍和翻译书籍 ...

  7. java的优点和误解 《java核心技术卷i》第一章

    <java核心技术卷i>第一章主要内容包括三点: 1:Java白皮书的关键术语:描述Java的十一个关键字: 2:Java applet 3 :关于Java的常见误解   1:第一章:Ja ...

  8. java JDK8 学习笔记——第11章 线程和并行API

    第11章 线程与并行API 11.1 线程 11.1.1 线程 在java中,如果想在main()以外独立设计流程,可以撰写类操作java.lang.Runnable接口,流程的进入点是操作在run( ...

  9. Java 螺纹第三版 第一章Thread介绍、 第二章Thread创建和管理学习笔记

    第一章 Thread导论 为何要用Thread ? 非堵塞I/O      I/O多路技术      轮询(polling)      信号 警告(Alarm)和定时器(Timer) 独立的任务(Ta ...

随机推荐

  1. css004 用样式继承节省时间

       css004 用样式继承节省时间 继承:inherit 继承可以简化样式表 继承是有局限的,有些样式没法继承,如:border,width,height

  2. mysql查询区分大小写

    Mysql默认查询是不分大小写的,可以在SQL语句中加入 binary来区分大小写: BINARY不是函数,是类型转换运算符,它用来强制它后面的字符串为一个二进制字符串,可以理解为在字符串比较的时候区 ...

  3. 自然语言12_Tokenizing Words and Sentences with NLTK

    https://www.pythonprogramming.net/tokenizing-words-sentences-nltk-tutorial/ # -*- coding: utf-8 -*- ...

  4. Linux下htop的使用

    linux top命令VIRT,RES,SHR,DATA的含义 第1行-第4行:显示CPU当前的运行负载,有几核就有几行,我的是4核 Mem:显示内存的使用情况,3887M大概是3.8G,此时的Mem ...

  5. NodeJS

    http://nodejs.org/ http://www.nodebeginner.org/index-zh-cn.html Javascript Engines http://www.sencha ...

  6. php 生成随机字符串 abcdeft....789

    ) { $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $str = &quo ...

  7. osharp3引入事务后操作结果类别的调整

    /// <summary> /// 表示业务操作结果的枚举, /// 对于业务务操作单元的影响只有二种状态, /// 成功,无变化: 操作将继续,事务将继续 /// 失败:将导致 操作被中 ...

  8. URL中“#” “?” &“”号的作用

    URL中"#" "?" &""号的作用   阅读目录 1. # 2. ? 3. & 回到顶部 1. # 10年9月,twit ...

  9. SmartUpLoad自动上传包

    一枚默默的开发学习者 用以下代码生成文件名即可 1 package info.haowei.util; 2 3 import java.text.SimpleDateFormat; 4 import ...

  10. Lua与C的交互

    Lua 与 C 的交互 Lua是一个嵌入式的语言,它不仅可以是一个独立运行的程序,也可以是一个用来嵌入其它应用的程序库. C API是一个C代码与Lua进行交互的函数集,它由以下几部分构成: 1.  ...