360彩票网站

实验:基于Java的多线程技术应用
作者:强官涛   类型:Java开发    类别:实验   日期:2018-11-27    阅读:1628 次   消耗积分:1 分

实验简介



多线程技术是任何一门编程语言所必备的基本特性,同时也是目前的应用程序普遍使用的一种提升执行效率的方法。本实验主要为大家讲解在Java中,如何应用多线程技术,进行并发用户的处理,以及在多线程应用过程中的一些特殊注意事项。

 


实验目的



1.理解Java的原生多线程技术的操作方法。

2.理解Java中多线程的生命周期。

3.理解多线程的同步,中断,优先级,等待,唤醒,合并,死锁等情况。

4.对Java当中的一些线程不安全的情况有所认知。


 

实验流程



1.进程与线程。


目前主流的操作系统基本上是多任务操作系统,允许计算机在同一时刻同时运行多个程序。操作系统中独立执行的程序被称为进程。例如在Windows操作系统中可以同时运行Microsoft Word和Microsoft PowerPoint两个程序,这两个程序就是两个不同的进程。进程占有系统资源,拥有资源使用权。我们可以在Windows的任务管理器中看到一个一个的进程运行情况,如下图所示:


20181127_104824_091.png


从上述任务管理器中我们可以看到,一个应用程序对应一个进程名称和进程ID号,也可以观察到这个应用程序对应的CPU,内在,磁盘和网络消耗情况。对于许多操作系统来说,一个进程中可包括一个或多个线程,线程是进程的实体,一个线程就是进程的一条执行路径,拥有多个线程的进程可以同时完成多种功能。例如在Microsoft Word中,我们可以边编辑文档边执行打印功能。多线程的并发执行通常是指在逻辑上的同时,并非物理上的同时。多线程程序设计中的各个线程彼此独立,这样各个线程可以乱序或交叉执行。

线程又被称为轻量级进程。较之于进程,线程相互之间的通信代价小,这使得多任务的线程比多任务的进程需要的开销要小。同一个进程中的多个线程共享内存等资源,这些线程可以访问相同的对象和变量,在使用进程时要注意对其它进程的影响。我们可以在任务管理器中打开资源监控器,看到每一个进程对应的线程数量及资源消耗情况,如下图所示:


20181127_104832_630.png

 

2.线程的生命周期。


一个线程从诞生到死亡整个生存期内存在9个基本状态:

(1)新建(Born) :新建的线程处于新建状态,new的时候即为新建。

(2)就绪(Ready) :在创建线程后,它将处于就绪状态,等待start()方法被调用。

(3)运行(Running):线程在开始执行时进入运行状态,当run()方法执行时

(4)睡眠(Sleeping):线程的执行可通过使用sleep()方法来暂时中止。睡眠后,线程进入就绪状态。

(5)等待(Waiting):如果调用了wait()方法,线程将处于等待状态。

(6)挂起(Suspended):在临时停止或中断线程的执行时,线程就处于挂起状态。

(7)恢复(Resume):在挂起的线程被恢复执行时,可以说它已被恢复

(8)阻塞(Blocked):在线程等待一个事件时(例如输入/输出操作),就称其处于阻塞状态。

(9)死亡(Dead):在run()方法已完成执行或其stop()方法被调用之后,线程就处于死亡状态。


20181127_104848_103.png

 

3.利用Thread类实现线程。


Thread类位于java.lang包中,实现java.lang.Runnable接口,继承java.lang.Object类。Thread是Java的进程管理的关键类,其中定义了大量的关于进程管理的操作方法,其常用方法如下:


方法定义

方法说明

public void run()

线程体所在位置,由Java虚拟机调用,通常被覆写的方法

public void start()

使该线程进入就绪态:Java虚拟机调用该线程的run()方法

public void interrupt()

中断进程,但事实是线程会继续执行

public static void yield()

暂停当前正在执行的线程对象,并执行其它线程

public static void sleep(long millis)

在millis毫秒数内让 当前正在执行的线程休眠(暂停执行)

public static void sleep(long millis,int nanos)

在millis毫秒数加nanos纳秒数内让当前正在执行的线程休眠(暂停执行):

public final void wait()

导致当前线程等待,直到其它线 程调用此对象的notify()方法或notifyAll() 方法

public final void wait(long timeout)

导致当前线程等待, 直到其它线程调用此对象的 notify() 方法或notifyAll() 方法,或者其它某个线程中断当前线程,或者超过timeout毫秒数

public final void wait(long timeout, int nanos)

致当前线 程等待,直到其它线程调用此对象的notify() 方法或 notifyAll() 方法,或者其它某个线程 中断当前线程,或者超过timeout毫秒数加nanos纳秒数

public final void notify()

唤醒在此对象监视器上等待的所有线程

public final void join()

等待该线程终止

public final void join(long millis)

等待该线程终止的时 间最长为 millis毫秒

public final void join(long millis, int nanos)

等待该线程 终止的时间最长为 millis 毫秒加 nanos纳秒。

 

在 Java 中实现多线程的方法之一是创建一个类并让该类继承 Thread 类并覆盖类中的run()方法(该方法没有任何参数),具体代码如下:


package com.woniuxy.thread;

 

public class MyThread extends Thread {

public static void main(String[] args) {

// 实例化当前类,并调用start()方法

MyThread mt = new MyThread();

mt.start();

}


// 重写父类的run方法,线程执行时会自动调用该方法

public void run() {

System.out.println("当前线程名称为:" + this.getName());

}

}

 

当我们运行上述方法后,会在控制台打印一次当前线程的名称。这相当于新建了一个线程,那么如何新建多个线程呢?

其实方法也非常简单,那就是直接利用循环即可完成多线程的创建,请看如下代码:


package com.woniuxy.thread;

 

public class MyThread extends Thread {

public static void main(String[] args) {

// 实例化当前类,并调用start()方法

for (int i=0; i<10; i++) {

MyThread mt = new MyThread();

mt.start();

}

}


// 重写父类的run方法,线程执行时会自动调用该方法

public void run() {

System.out.println("当前线程名称为:" + this.getName());

}

}

 

上述代码中我们为MyThread类创建了10个线程来运行,运行结果的输出内容如下:


当前线程名称为:Thread-0

当前线程名称为:Thread-2

当前线程名称为:Thread-1

当前线程名称为:Thread-3

当前线程名称为:Thread-5

当前线程名称为:Thread-4

当前线程名称为:Thread-6

当前线程名称为:Thread-8

当前线程名称为:Thread-7

当前线程名称为:Thread-9

 

我们可以看到,线程的运行并非按顺序执行的,而是具备随机性。上述的线程状态经历了新建,就绪,运行和死亡四种状态,这也是一个线程常见的几种最基本的状态。此处我们需要注意的是,循环创建的MyThread实例是10个,对应于新建了10个线程,相应的每一个线程只运行1次,就叫多线程单循环,与我们平时的一个实例循环运行10次是完全不一样的效果。比如如果我们将上述代码的循环放在run()方法中,代码如下:


public class MyThread extends Thread {

public static void main(String[] args) {

MyThread mt = new MyThread();

mt.start();

}

 

public void run() {

for (int i=0; i<10; i++) {

System.out.println("当前线程名称为:" + this.getName());

}

}

}

 

则最终的运行效果会变为一个线程的运行情况,线程名称只有一个:


当前线程名称为:Thread-0

当前线程名称为:Thread-0

当前线程名称为:Thread-0

当前线程名称为:Thread-0

当前线程名称为:Thread-0

当前线程名称为:Thread-0

当前线程名称为:Thread-0

当前线程名称为:Thread-0

当前线程名称为:Thread-0

当前线程名称为:Thread-0

 

4.利用Runnable接口实现线程。


除了上述通过继承Thread类构造线程对象外,Java还提供了通过Runnable接口获得线程对象的方法。Runnable接口位于java.lang包中,只有一个run()方法。实现Runnable 接口的类通过实例化一个对象并将这个对象作为运行目标,就可以运行线程而无需创建Thread的子类。Runnable的接口与Thread超类的使用大致相同,以MyThread为例,其代码可以这样修改:


package com.woniuxy.thread;

 

public class MyRunnable implements Runnable {

public static void main(String[] args) {

MyRunnable mr = new MyRunnable();


for (int i=0; i<10; i++) {

Thread t = new Thread(mr);

t.start();

}

}


@Override

public void run() {

// 由于MyRunnable没有父类,我们不能直接使用this.getName方法

// 来获取当前线程的名称,而应该使用更加通用的方式获取

System.out.println("当前线程名称为:" +

              Thread.currentThread().getName());

}

}

 

上述代码的输入与MyThread类的输出是一模一样的。当然,通常情况下,我们并不建议在自己的类当中并发自己的类实例作为线程,这样比较容易搞混淆,技术上虽然是没有任何问题的。本书对于一些简单的知识点,将采取内部类的方式来执行,这样也会比较方便大家区分。

 

5.主线程与子线程。


通常情况下,任何一段Java代码都是通过main()方法来开始运行的,这个时候JVM或开启一个主线程用于执行main()方法及后续被调用的程序。

所以,我们将运行Java代码的这一个线程称之为主线程,而由主线程在执行代码过程中开启的线程称之为子线程。

但是这里需要特别注意的是,主线程一旦产生了子线程,那么子线程的命运便交由CPU来处理了,主线程与子线程再无关系,各自运行各自的任务,运行完成该结束就结束。但是对于进程而言,必须要等到所有的线程都运行结束后,进程才能正常结束。我们来看看下面一段代码:


package com.woniuxy.thread;

 

public class MainThread {

public static void main(String[] args) {

for (int i=1; i<=10; i++) {

// 通过SubThread构造参数指定线程名称

SubThread sub = new SubThread("子线程-" + i);

Thread t = new Thread(sub);

t.start();

}

// 主线程main方法正常运行,但是并不一定在最后运行

System.out.println("这是主线程在运行:" +

Thread.currentThread().getName());

}

}

 

class SubThread implements Runnable {

private String threadName = "";

public SubThread() {


版权所有,转载本站文章请注明出处:蜗牛学院在线课堂, http://quangtruong.net/note/237
提示:登录后添加有效评论可享受积分哦!

        • <form id='zsoeh'></form>
            <bdo id='zsoeh'><sup id='zsoeh'><div id='zsoeh'><bdo id='zsoeh'></bdo></div></sup></bdo>