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

Java线程学习笔记之线程简介

2017-02-15 17:24 423 查看

什么是线程

几乎每一种操作系统都支持进程的概念——进程就是在某种程度上相互隔离的、独立运行的程序。



线程化是允许多个活动共存于一个进程中的工具。大多数现代的操作系统都支持线程,而且线程的概念以各种形式已经存在了好多年。Java是第一个在语言本身中显式地包含线程的主流编程语言,他没有把线程化看做是底层操作系统的工具。

有时候,线程也称作轻量级进程。就像进程一样,线程在程序中是独立的、并发的执行路径,每个线程都有它自己的堆栈、自己的程序技术器和自己的局部变量。但是,与分隔的进程相比,进程中的线程之间的隔离程度要小。它们共享内存、文件句柄和其他每个进程应有的状态。

进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。

每个Java程序都至少有一个线程——主线程。当一个Java程序启动时,JVM会创建主线程,并在该线程中调用程序的main()方法。

创建线程

在Java程序中创建线程有几种方法。每个Java程序至少包含一个线程:主线程。其他线程都是通过Thread构造器或实例化继承类Thread的类来创建。

例如通过实例化继承类创建一个线程:

public class Test extends Thread{

public static final int Max_r = 1000000;
public static final int Ten_s = 10000;

public volatile boolean finished = false;
//必须要实现run方法,因为线程要直接调用run方法,执行run方法里面的逻辑
public void run(){
int[] prims = new int[Max_r];
int count = 0;

for(int i=2; count<Max_r;i++){
if(finished)
break;

boolean prime = true;
for(int j = 0; j<count;j++){
if(i%prims[j]==0){
prime = false;
break;
}
}

if(prime){
prims[count++] = i;
System.out.println("Found prime: "+i);
}
}
}

public static void main(String[] args){
Test test = new Test();//实例化继承了Thread类的Test类,新建一个线程
test.start();//执行线程
try{
Thread.sleep(5000);//使线程休眠5秒
}
catch(InterruptedException e){

}

test.finished = true;
}
}


在一个线程对新线程的Thread对象调用start()方法之前,这个新线程并没有真正开始执行。Thread对象在其线程真正启动之前就已经存在了,而且其线程退出之后仍然存在。

结束线程

线程会以以下三种方式之一结束:

线程到达其run()方法的末尾

线程抛出一个未捕获的Exception或Error

另一个线程调用一个弃用的方法——stop()方法。

加入线程

Thread API包含了一个等待另一个线程完成的方法:join()方法。当调用Thread join()方法时,调用线程将阻塞,直到目标线程完成为止。

还有,永远不要假设一个线程会在另一个线程之前执行某些操作,除非您已经使用了同步以强制一个特定的执行顺序。因为不同的机器执行的顺序不同,即使是同一台机器,相同的代码执行顺序也不同。可以通过这个例子来看:

public class TenThread {
private static class WorkerThread extends Thread {
int rdata;

public WorkerThread(int rdata) {
this.rdata = rdata;
}

public void run(){
rdata = rdata + 100;
System.out.println(Thread.currentThread().getName()+":"+rdata);
}

public int getRdata() {
return rdata;
}

public void setRdata(int rdata) {
this.rdata = rdata;
}

}

public static void main(String[] args){
WorkerThread[] threads = new WorkerThread[10];
int max = Integer.MIN_VALUE;

for(int i=0;i<10;i++){
threads[i] = new WorkerThread((int)(Math.random()*100));
threads[i].start();
}

try{
for(int i=0;i<10;i++){
threads[i].join();
System.out.println(threads[i].getName()+"————"+threads[i].getRdata());
max = Math.max(max, threads[i].getRdata());
}
}catch(InterruptedException e){

}
System.out.println("Maximum value was " + max);
}
}


执行结果:

Thread-1:153
Thread-9:114
Thread-8:143
Thread-0:118
Thread-6:116
Thread-7:177
Thread-5:196
Thread-4:171
Thread-2:168
Thread-3:114
Thread-0————118
Thread-1————153
Thread-2————168
Thread-3————114
Thread-4————171
Thread-5————196
Thread-6————116
Thread-7————177
Thread-8————143
Thread-9————114
Maximum value was 196


再执行一次:

Thread-0:139
Thread-2:143
Thread-4:163
Thread-1:164
Thread-3:124
Thread-5:197
Thread-6:114
Thread-7:196
Thread-8:114
Thread-0————139
Thread-1————164
Thread-9:170
Thread-2————143
Thread-3————124
Thread-4————163
Thread-5————197
Thread-6————114
Thread-7————196
Thread-8————114
Thread-9————170
Maximum value was 197


休眠

Thread API包含了一个sleep()方法,它将使当前线程进入等待状态,知道过了一段时间,或者知道另一个线程对当前线程的Thread对象调用了Thread.interrupt()方法,从而中断了线程。当过了指定时间后,线程又将变成可运行的,并且回到调度程序的可运行线程队列中。

如果线程是由对Thread.interrupt()的调用而中断,那么休眠的线程会抛出InterruotedException,这样线程就知道它是由中断唤醒的,就不必查看计时器是否过期。例如通过下面的代码唤醒线程并捕获InterruotedException异常:

import java.util.Date;

public class TwoThread {

public static class Thread1 extends Thread {
public void run() {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"live");//捕获异常
}
}
}

public static class Thread2 extends Thread {
public void run() {
for(int i=0;i>=0;i++){
System.out.println(new Date());
}
}
}

public static void main(String[] args) {
Thread2 t2 = new Thread2();
t2.start();
Thread1 t1 = new Thread1();
t1.start();
try{
Thread.sleep(2000);
}catch (InterruptedException e) {
// 这里捕获的异常就不执行其他代码了
}
t1.interrupt();
t2.stop();
}
}


Thread.yield()方法就像sleep()一样,但它并不引起休眠,而是暂停当前线程片刻,这样其他线程就可以运行了。在大多数实现中,当较高优先级的线程调用Thread.yield()时,较低优先级的线程就不会运行。

守护程序线程

们提到过当 Java 程序的所有线程都完成时,该程序就退出,但这并不完全正确。隐藏的系统线程,如垃圾收集线程和由 JVM 创建的其它线程会怎么样?我们没有办法停止这些线程。如果那些线程正在运行,那么 Java 程序怎么退出呢?

这些系统线程称作守护程序线程。Java 程序实际上是在它的所有非守护程序线程完成后退出的。

任何线程都可以变成守护程序线程。可以通过调用 Thread.setDaemon() 方法来指明某个线程是守护程序线程。您也许想要使用守护程序线程作为在程序中创建的后台线程,如计时器线程或其它延迟的事件线程,只有当其它非守护程序线程正在运行时,这些线程才有用。

谁会创建线程

线程通过几种机制进入java程序。除了用Thread构造器显式创建线程之外,还可以用许多其他的机制来创建线程:

AWT和Swing

RMI

java.util.TimerTask工具

servlet和JSP技术

从jdk1.3开始。TimerTask工具被引入到Java语言。这个便利的工具可以让你在稍后的某个时间执行任务或者定期执行任务。

实现Timer类非常简单:创建一个计时器线程,并且构建一个安执行时间排序的等待时间队列。TimerTask线程被标记成守护程序线程,这样他就不会阻止程序退出。

Timer是守护线程。

因为计时器时间实在计时器线程中执行,所以必须确保正确同步了针对计时器任务中使用的任何数据项的访问。Timer类是线程安全的。

package threadtest;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTaskTest extends Thread{

@Override
public void run() {
// TODO Auto-generated method stub
Timer timer = new Timer("timerThread");
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date())+"---"+Thread.currentThread().getName());
}
};
timer.schedule(timerTask, 10000, 20000);//设置创建任务10秒后开始执行,之后每隔20秒执行一次任务
}

public static void main(String[] args){
TimerTaskTest test = new TimerTaskTest();
test.setDaemon(true);
test.start();
try {
Thread.sleep(120000);//让主线程休眠一段时间,从而让新建的线程有时间执行,当主线程关闭的时候,其他线程也会关闭
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("主线程关闭:"+Thread.currentThread().getName());
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: