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

Java 线程的初识

2017-11-07 21:34 381 查看
文章目录

一、进程与线程

1、什么是进程

2、什么是线程

3、线程和进程之间的关系

4、进程和线程内存方法的知识

5、线程的分类

二、多线程存在的意义

1、提高程序的执行效率

2、充分利用cpu资源

三、线程的创建

1、使用Thread 类创建线程

2、实现 Runnalbel 接口

四、线程的运行状态

1、 被创建

2、运行

3、冻结(sleep和wait)

4、阻塞(临时状态)

5、消亡

五、线程的基本属性

1、线程栈

2、线程名

3、获取当前线程

...


一、进程与线程

1、什么是进程

正在进行中的程序。 例如 : 迅雷就是一个进程。 每一进程都有一个执行路径(或者叫控制单元)

2、什么是线程

进程中的一个独立控制单元(执行路径),每一个进程中,必须至少建立一个线程来作为这个程序运行的起点。

例如: 迅雷同时下载多个视频文件。

3、进程与线程之间的关系

进程其实就是对线程的封装(线程是进程中的内容,线程在控制着进程的执行),操作系统将进程分成多个线程后,这些线程可以在操作系统的管理下并发执行。

虽然线程宏观上多个线程同时执行,但实际上这只是操作系统在障眼法。由于一块cpu同时执行执行一条命令(多核除外),因此在拥有一块cpu的计算机上不可能同时执行两个任务。操作系统为了提高程序的运行效率,在一个线程空闲时会撤下这个线程,并且让其他线程来执行(线程调度)。

其实,就是cpu 在多个线程中快速切换,由于速度快,我们很难感知,所以看上去像是并发执行。

4、进程和线程内存方法的知识

系统存在多个进程,进程之间不共享内存,进程中的线程共享系统分配给这个进程的内存空间。

每个线程也拥有自己独立的内存空间,这段空间叫 “内存栈” 。在x86平台下,默认线程栈大小是12KB

5、线程的分类

主线程 : 每个进程有且仅有一个主线程,主线程是程序执行的入口(主线程的代码放于main方法中)

子线程 : 主线程除外的其他执行路径即为子线程(子线程的代码放于run() 方法中 )

二、线程存在的意义

1、提高程序的执行效率

2、充分利用cpu资源



三、线程的创建

1、通过Thread类创建线程

步骤如下 :

a. 定义类继承Thread

b. 重写无参run() 方法,并将要在子线程执行的代码放于run()方法体内

c. 创建自定义类对象,调用自定义对象的start()方法,让虚拟机调用线程的run方法。

例如:

定义子类继承 Thread

class Demo extends Thread
{
public void run()
{
// do something
}
}


创建线程

public static void main (Stringh[] args)
{

Demo thread = new Demo();  // 创建线程
thread.start();            // 启动线程 并调用 run()

}


注意: 如果直接调用对象的run() 方法,任务不会在子线程执行。子线程创建了,但是并没有运行。即 start() 方法的作用是:(1)启动线程 (2)并执行线程的run方法

换句话说 : 多个线程在争夺cpu的使用权,谁争取到,谁执行(随机性)。在某一时刻,只能有一个程序运行(多核除外)。cpu在做快速切换,以达到看上去是同时运行的效果。

2、实现 Runnalbel 接口

步骤如下 :

a. 自定义类实现Runnable接口,并重写run()方法。

b. 建立一个Thread对象

c. 将Runnable 接口的子类对象作为实际参数传递给Thread类的构造函数

d. 调用Thread 类的start 方法开启线程

例如 :

实现Runnable 接口

class Demo implenention Runnable
{
public void run()
{
//do something
}
}


main函数

public static void main (Stringh[] args)
{

Demo thread = new Demo();    // 创建线程
new Thread(thread).start();  //启动线程 并调用 run()

}


注意:第二种方法扩展性比较高,可以有自己的父类。又可以实现Runnable。避免单继承的局限性。

四、线程的运行状态

1、 被创建

2、运行

3、消亡

4、冻结(seleep和wait)

5、阻塞


总结如图下图



线程在创建后, 并不会立即执行run方法中的代码,而是处理等待状态。线程处于等待状态时,可以通过Thread类的方法设置线程的各种属性。

当调用start()方法后,线程开始执行run方法中的代码,线程进入运行状态。可以通过isAlive() 方法来判断线程是否处于运行状态。 当返回true时,线程处于运行状态。当返回flase,可能处于等在状态,也可能处于停止状态。

例子: (线程创建、运行、消亡)

public class LifeCycle extends Thread
{
public void run()
{
int i = 0;
while((++n)< 1000);
}

public static void main (String[] args)
{
LifeCycle  thread = new LifeCycle();

System.out.println(threat.isAlive());  // 测试线程是否处于活动状态   flase

thread.start(); //开始线程

System.out.println(threat.isAlive());  // 测试线程是否处于活动状态   true

thread.join();  // 等待该线程终止,即run方法执行结束,线程消亡 (或理解成退出状态完成)

System.out.println("线程结束了");

System.out.println(thread.isAlive());    // 测试线程是否处于活动状态   flase
}
}


线程的挂起和唤醒: 即等待状态,线程暂时进入等在状态。当等待时间到了,线程可能处于临时状态(阻塞状态),也可能处于运行状态。

例如:

public class ThreatSleep extends Thread
{
public void run()
{
try
{
sleep(2000);

System.out.println("2s后可能执行此行代码");

}
catch (InterruptedException e)
{

e.printStackTrace();
}

}

public static void main(String[] args) {

ThreatSleep thread = new ThreatSleep();
thread.start();

}


注意 : sleep() 只对当前正在执行的线程起作用。不能再一个线程中休眠另一个线程。例如: 在main方法中使用thread.sleep(2000)方法是无法使thread线程休眠2秒,而只能使主线程休眠2秒。

线程的消亡 : 线程任务执行完成后,自动消亡。 可理解成run()方法执行结束。

在java中一般有3种方法终止线程:

(1`) 使用退出标志,使线程正常退出,也就是当run方法完成后终止线程

(2) 使用interrupt() 方法终止线程

(3) 使用stop() 方法强行终止线程(不推荐使用,可能会发生不可预料的后果)

当执行run()方法完成后,线程就会退出,但是有时候run()方法是永远都不会结束的。例如在服务端程序中使用线程进行监听客户端的请求,或者其他是需要循环处理的任务,在这种情况下,一般是将这些任务放在一个循环中,例如while(true){..}来处理。但是如果想使while循环在特定条件下退出,最直接的情况是设置一个boolean类型的标志位,并通过设置这个标志位为true或flase来控制while循环是否退出


例如:

public class ThreadStop extends Thread{

// volatile 使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值
public volatile boolean exit = false;

public void run()
{
while(!exit){

System.out.println(this.getName());
}
}

public static void main(String[] args) throws InterruptedException
{

ThreadStop thread = new ThreadStop();
thread.start();
sleep(1000); //主线程睡眠1s,下面的代码延迟1s后执行
thread.exit = true;
thread.join(); // 等待thread线程执行完成再继续执行下面的代码
System.out.println(thread.isAlive());

}

}


使用stop()方法可以强行终止正在运行或者挂起的线程。但是使用stop()方法是很危险,就像突然关闭计算机电源,可能会产生不可预料的结果。因此不推荐使用stop()方法来终止线程。

在使用interrupt()方法终止线程时可分为下面两种情况

( 1 ) 线程处于阻塞状态,如使用sleep()方法

( 2 ) 使用while(! isInterrupted){….} 来判断线程是否被中断

上述第一种情况下使用interrupt方法,sleep方法将判处一个InterruptedException ,而第二种情况下线程将直接退出。

补充 : Thread有两个方法可以判断线程是否是通过interrupt()方法被终止。一个是静态方法interrupted(),另一个是非静态方法isInterrupted()。前者用来判断当前线程是否被中断,后者则可以判断其他线程是否被中断。

例如:

public class ThreadStop extends Thread {

public void run()
{
try
{
sleep(50000);
}
catch (InterruptedException e)
{
System.out.println(e.getMessage());
}
}

public static void main(String[] args) throws IOException, InterruptedException
{
ThreadStop thread = new ThreadStop();

thread.start();

System.out.println("在50s内按任意键中断线程");

System.in.read();

System.out.println("0..."+thread.isAlive());

thread.interrupt();

//判断thread线程是否是通过 interrupt 被终止的
if(thread.isInterrupted())
System.out.println("是的,我被interrupt终止了");

System.out.println("1..."+thread.isAlive()); //线程被打断后,不是立即就挂了

thread.join(); //等待线程 终止状态完成 后 再继续执行下面的代码

System.out.println("2..."+thread.isAlive());

System.out.println("线程已经退出");

}

}


备注: isAlive() 可以用来测试某条线程是否已经死亡。当线程处于就绪、运行、或者阻塞3种状态时,该方法返回true。 当线程处于创建、死亡状态时,该方法返回false。

线程死亡 : 线程可以使用下面3种方式来结束线程,结束后的线程处于死亡状态。

( 1 ) run() 方法执行完成,线程正常结束

( 2 ) 线程抛出一个未捕获的异常 Exception 或 Error

( 3 ) 直接调用该线程的stop() 方法结束该线程,因为该方法容易导致死锁,所以不推荐使用。

注意 : 不要试图对一个已经死亡的线程调用start()方法使他重新启动,死亡就是死亡,该线程将不可再次作为线程执行。

线程阻塞 : 当线程开始运行后,它不可能一直处于运行状态,除非他的线程执行时间体足够短,瞬间就执行结束了。线程在运行过程中被中断,目的是使其他线程获得执行的机会,线程的调度细节取决于平台所采用的策略。对于采用抢占式策略的系统而言,系统会给每个可执行的线程一个小时间段来处理任务,当该时间段用完,系统就会剥夺该线程所占用的资源,让其他线程获得执行权,在选择下一个线程时,系统会优先考虑线程的优先级。所有现代的桌面和服务器操作系统都是采用抢占式调度策略,但一些小型设备入手机才会采用协作式调度,在这样的系统中,只有当一个线程调用了自身的sleep()或yield()方法后才会放弃所占用的资源(就是必须由该线程主动放弃所占用的资源)

在计算机中,当发生如下状态时线程就会进入阻塞状态

(1) 线程调用了sleep()方法主动放弃所占用的处理器资源

( 2 ) 线程调用了一个阻塞式 I/O 方法,在该方法返回之前,该线程被阻塞

(3) 线程正在等待某一个通知(notify)

(4) 当前正在执行的线程别阻塞后,其他线程就可以获得执行的机会。被阻塞的线程会在合适的机会重新进入就绪状态。注意,就绪状态而不是运行状态。也就是说被阻塞线程的阻塞解除后,必须重新登台线程调度器再次调度他。

(5)线程试图获得一个同步监视器,但该监视器正在被其他线程所持有

线程死亡: 线程执行任务结束

(1)run 方法执行完成,线程正常结束

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

( 3 ) 直接调用线程的stop() 方法 结束线程。

注意 : 对已经死亡的线程,不可将该线程再次作为线程执行。否则会抛出ILLegalThreadStateException异常。

五、线程的基本属性

1、线程栈

系统中的进程是在各自独立的内存中运行的,进程中的线程共享系统分配给这个进程的内存空间,而且线程还拥 有自己的内存空间,这段内存空间称为”线程栈”

2、线程名

线程都有自己默认的名称,Thread-编号,编号从0开始。用getName()获取。

当然,你也可以自定义名称,用setName(String name) 设置。当然,你也可以通过构造方法去设置。

主线程名字为: main

3、获取当前线程

Thread.currentThread()

currentThread() 是Thread类静态方法,该方法总是返回当前正在执行的线程对象


4、判断线程是否处于运行状态

thread.isAlive(), 返回 true,线程处于运行状态。 返回false,线程处于等待状态,也可能处于停止状态。

5、使用join() 方法

使异步执行的线程变成同步执行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: