您的位置:首页 > 编程语言 > Java开发

java并发编程之线程管理

2016-12-14 18:44 453 查看

1:简介

在计算机领域中,我们说的并发(Concurrency)是指一系列任务的同时运行。如果一台电脑有多个处理器或者有一个多核处理器,这个同时性(Simultaneity)是真正意义的并发;但是一台电脑只有一个单核处理器,这个同时性并不是真正的并发。现代操作系统都允许多任务的并发执行。在听歌的时候,你可以同时阅读电子邮件,也可以同时阅读网页上的信息。这种并发是进程级(Process-Level)并发。但在一个进程内也可以有多个同时进行的任务。这种进程内并发的任务成为线程(Thread)。

1.1:线程的创建和运行

java提供了两种方式来创建线程
  继承Thread类 并且覆盖run方法
  创建一个实现Runnable接口的类 使用Thread的构造器来创建Thread对象 这个参数就是实现Runnable接口的类的一个对象

新建立一个Calculator类实现Runnable接口

public class Calculator implements Runnable {

private int number;
public Calculator(int i) {
// TODO Auto-generated constructor stub
this.number=i;
}

@Override

public void run() {
// TODO Auto-generated method stub
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{

/**
* @param args 在main方法中 创建了一个执行十次的循环 每次循环中创建一个Calculator 对象 一个Thread对象 并把Calculator 对象作为构造器的参数传入
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
for(int i=0;i<10;i++){
Calculator calculator=new Calculator(i);
Thread thread=new Thread(calculator);
thread.start();
}
}

}

可以看到我们创建的10个线程的运行情况,它们并行的执行既定任务,并将结果显示出来。

每个java程序至少有一个执行线程 当运行程序的时候,jvm将启动这个执行线程来调用main方法,当调用Thread对象的start方法时 另一个执行线程被创建 因而在这个程序中每次调用start方法 都将会创建一个执行线程 当一个程序的所有线程都执行完成时 这个java程序结束
如果是执行main方法的线程结束了,其他线程任然继续执行知道他们运行结束 如果某个线程调用了System.exit()指令来解释程序的执行 ,所有线程都将结束

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

1.2:线程信息的获取和设置

Thread类有一些保存信息的属性
这些属性可以用来标识线程,显示线程的状态或者控制现场的优先级

ID:保存了线程的唯一标识符

Name:保存了线程的名称

Priority:保存了线程对象的优先级。线程的优先级从一到十,其中一最低;

Status:保存了线程的状态
在java中 线程的状态有六种 new,runnable.blocked,waiting.time waitting,terminated

编写Calculator类实现Runnable接口
public class Calculator1 implements Runnable {

private int number;

public Calculator1(int number) {
this.number=number;
}
//这个方法用来执行我们创建的线程的指令,对指定的数组进行乘法表运算
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) {
 
System.out.printf("Minimum Priority: %s\n",Thread.MIN_PRIORITY);
System.out.printf("Normal Priority: %s\n",Thread.NORM_PRIORITY);
System.out.printf("Maximun Priority: %s\n",Thread.MAX_PRIORITY);
//创建线程数组
Thread threads[];
//创建线程状态数组 用来保存线程运行时的状态
Thread.State status[];
threads=new Thread[10];
status=new Thread.State[10];
//创建一个容量为10的Calculator对象数组 为每个对象都设置不同的数字 然后将他们作为Thread
//构造器的参数来创10建线程对象,并将其中五个线程的优先级设置为最高,其他五个设置为最低
for (int i=0; i<10; i++){
threads[i]=new Thread(new Calculator1(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 file = new FileWriter(".\\data\\log.txt");PrintWriter pw = new PrintWriter(file);){
//讲10个线程的状态写入文件中 现在线程状态是new
for (int i=0; i<10; i++){
pw.println("Main : Status of Thread "+i+" : "+threads[i].getState());
status[i]=threads[i].getState();
}

//执行10个线程
for (int i=0; i<10; i++){
threads[i].start();
}
//10个线程执行完成后 查看线程状态 如果状态发生变化就写入到文件中 
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();
}
}
//状态发生变化的线程 将现场的ID 名称 优先级 旧的状态和新的状态写入到文件中
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");
}

}

这是部分运行结果
Thread 3: 3 * 5 = 15
Thread 3: 3 * 6 = 18

Thread 3: 3 * 7 = 21

Thread 5: 5 * 9 = 45

Thread 9: 9 * 4 = 36

Thread 9: 9 * 5 = 45

Thread 9: 9 * 6 = 54

Thread 5: 5 * 10 = 50

Thread 3: 3 * 8 = 24

Thread 3: 3 * 9 = 27

Thread 9: 9 * 7 = 63

Thread 9: 9 * 8 = 72

Thread 9: 9 * 9 = 81

Thread 9: 9 * 10 = 90

Thread 3: 3 * 10 = 30

可以看到优先级高的比优先级低的运行先结束
我们也可以在log.tex文件中看到每个线程状态的演变1.3线程的中断

Java提供了中断机制,我们可以使用它来结束一个线程。这种机制要求线程检查它是否被中断了,然后决定是不是响应这个终止请求。线程允许忽略这个终止请求并且继续执行

创建一个名为PrimeGenerator的类,并继承Thread类。

public class PrimeGenerator extends Thread{

@Override
public void run() {
long number=1L;

while (true) {
 
               
if
(isPrime(number)) {
System.out.printf("Number %d is Prime\n",number);
}
//一个数字处理完成过后 调用isInterrupted方法来检查线程是不是中断了 如果isInterrupted返回值是true,就结束线程执行

if
(isInterrupted()) {
System.out.printf("The Prime Generator has been Interrupted\n");
return;
}
number++;
}
}
 
   
private boolean isPrime(long number) {
if (number <=2) {
return true;
}
for (long i=2; i<number; i++){
if ((number % i)==0) {
return false;
}
}
return true;
}

}
编写主类
public
class Main {

//让现场睡5秒后 中断PrimeGenerator线程;

public static void main(String[] args) {

Thread task=new PrimeGenerator();
task.start();

try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}

task.interrupt();
}

}

//注意:Thread类中有一个表明线程被中断与否的属性
他存放的是boolean值 线程的interrupt()方法被调用时这个属性就会被设置为true;isInterrupted()方法只是返回这个属性的值。

1.4:线程的休眠和恢复

有些时候,你需要在某一个预期的时间中断线程的执行。例如,程序的一个线程每隔一分钟检查一次传感器状态,其余时间什么都不做。在这段空闲时间,线程不占用计算机的任何资源。当它继续执行的时钟来临时,JVM会选中它来继续执行。可以通过线程的sleep()方法来达到这个目标。sleep()方法接受整型数值作为参数,以表明线程挂起执行的毫秒数。当线程休眠的时间结束了,JVM会分给它CPU时钟,线程将继续执行它的指令。

sleep()方法的另一种使用方式是通过TimeUnit枚举类元素进行调用。这个方法也是使用Thread类的sleep()方法来使当前线程休眠,但是它接收的参数是秒,最后会被转化成毫秒。

创建一个FileClock类并实现Runnable接口

public class FileClock implements Runnable {

@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
System.out.printf("%s\n", new Date());
try {
//让当前线程睡一秒 当线程休眠时间结束了 会继续执行
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
System.out.println("The FileClock has been interrupted");
}
}
}

}

编写主类
public class FileMain {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// to run it
FileClock clock=new FileClock();
Thread thread=new Thread(clock);

// Starts the Thread
thread.start();
try {
//让主线程睡2秒
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
};
// Interrupts the Thread
//执行线程中断
thread.interrupt();
}

}

运行结果如下
Wed Dec 14 19:37:57 CST 2016

Wed Dec 14 19:37:59 CST 2016

The FileClock has been interrupted

Wed Dec 14 19:37:59 CST 2016

Wed Dec 14 19:38:00 CST 2016

Wed Dec 14 19:38:01 CST 2016

可以看到出现每隔一秒就会输出当前世界,接下来是FileClock线程中断已经被中断的信息 当调用sleep方法之后 线程会释放cpu不在继续执行任务 在这段时间内线程不占用cpu 所以cuo可以执行其他任务

1.5:等待线程的终止

在一些情况下 我们必须等待线程的终止 例如 我们的程序在执行其他的任务时 必须先初始化一些必要的资源 可以使用线程来完成这些初始化任务 等待线程的终止 在执行接下来的任务 为了达到这个目的 我们使用Thread的join方法 当一个线程对象的join被调用时 调用他的线程将挂起 知道这个线程任务执行完成

创建名为DataSourcesLoader的类并实现Runnable接口
//这个方法显示了当前线程开始执行的信息 睡眠4秒后显示其他信息 表示已经完成当前执行
public class DataSourcesLoader implements Runnable {

@Override
public void run() {

System.out.printf("Beginning data sources loding: %s\n",new Date());
try{
TimeUnit.SECONDS.sleep(4);
}catch (InterruptedException e) {

e.printStackTrace();
}
          System.out.printf("Data sources loading has finished:%s\n",new Date());
}

}

创建NetworkConnectionsLoader类并实现runnable接口
public class NetworkConnectionsLoader implements Runnable {

@Override
public void run() {
System.out.printf("Begining network connections loading: %s\n",new Date());
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Network connections loading has finished: %s\n",new Date());
}

}

编写主类
public class Main {
public static void main(String[] args) {
DataSourcesLoader dsLoader = new DataSourcesLoader();
Thread thread1 = new Thread(dsLoader,"DataSourceThread");
thread1.start();
NetworkConnectionsLoader ncLoader = new NetworkConnectionsLoader();
Thread thread2 = new Thread(ncLoader,"NetworkConnectionLoader");
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("Main: Configuration has been loaded: %s\n",new Date());
}

}

运行结果如下
Beginning data sources loding: Wed Dec 14 19:48:28 CST 2016

Begining network connections loading: Wed Dec 14 19:48:28 CST 2016

Data sources loading has finished:Wed Dec 14 19:48:32 CST 2016

Network connections loading has finished: Wed Dec 14 19:48:34 CST 2016

Main: Configuration has been loaded: Wed Dec 14 19:48:34 CST 2016

两个子线程同时运行 thread1.join(); thread2.join();当主线程执行到这里来时
会等待thread1子线程执行完 当thread1子线程在执行的时候 main主线程将会被挂起 知道thread1执行完成 才继续向下执行
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息