Java中的多线程编程

http://www.cnblogs.com/baoguo/articles/826534.html

 

 

Java中的多线程编程

主要内容

初识Java多线程

线程的生命周期

Java多线程程序设计方法

多线程应用示例

线程的优先级、同步*

 

初识多线程

一个简单的多线程的示例

class ManyThread extends Thread{

  int n=0;

 

  public static void main(String[] aa){

     ManyThread t1=new ManyThread();

     ManyThread t2=new ManyThread();

     System.out.println("Thread1 start!");

     t1.start();

     System.out.println("Thread2 start!");

     t2.start();

     System.out.println("Ok");

  }

  public void run(){

     while(n<10){

        n++;

      System.out.println(currentThread().getName()+"/t"+n);

     }

  }

}

结论:

²       线程类名为Thread,可由Thread及其子类创建线程对象。

²       可通过Thread.currentThread()可测得当前线程

²       线程是由start启动的,启动后会自动执行run方法。

 

单线程和多线程

线程:一个程序中彼此分离的、能独立运行的子任务。

单线程和排队等候

没有特意创建线程的程序为单线程程序, Java能自动创建和控制线程。

单线程的本质是排队等候,一个任务必须在另一个任务之后执行。

资源利用率低,CPU在大多时候空闲

 

多线程和并行执行

多线程的程序将任务分成几个子任务,多个子任务并行操作。(抢占式)

 

 

思考

1.    多线程程序的运行方式是?

2.    一个任务运行完毕,再执行另一个任务,这是单线程还是多线程?

3.    多线程的程序有什么优越之处?

4.    哪种程序有更好的用户响应能力?

5.    线程是如何启动的?

6.    线程启动后具体执行的方法是?

7.    在多线程工作时,怎么测得当前占据CPU运行时刻的线程?

 

线程的生命周期

在一个线程的生命周期中,它总处于某一种状态中。

Thread类及其主要方法

构造方法

public Thread()用缺省名称创建一个Thread对象

public Thread(String name)用指定名称创建一个Thread对象

 

线程的常用方法

说明

任务

public void start()

启动一个线程

public void run()

执行线程

 

休眠

public void stop()

停止线程

public static Thread currentThread()

必须throws InterruptedException

返回当前线程

public String getName()

返回线程的名称

 

线程的状态

线程的状态通常可归纳为:新生态运行态不可运行态死亡态四种状态。

新生态

时机:当创建了类Thread或其子类的实例对象时,一个新的线程就产生了。

ManyThread t1=new ManyThread()

线程去向:只能启动或终止该线程。调用start()stop()之外的其它方法都会失败并且会引起非法状态处理。

 

运行态( Runnable )

时机:对新生态的线程执行start()方法进入运行态,此时该线程处于可运行状态。

t1.start();

线程去向:进入不可运行的状态(等待或睡眠)和死亡态。

说明

²       处于运行态的线程都在运行或侍机运行。

²       单处理器的计算机,每一时刻真正处于运行中的线程只有一个。

²       JavaVM实现调度来保证这些线程共享处理器。

 

不可运行态

时机:对运行态中的线程进行了如下处理后,线程处于不可运行状态。

²       调用了sleep()方法;

²       调用了suspend()方法(线程被挂起)

²       为等候一个条件变量,线程调用wait()方法;

²       输入输出流中发生线程阻塞。

try {

Thread.currentThread().sleep(10000);

} catch (InterruptedException e){}

线程去向:进入运行态或死亡态。

 

四种不可运行态有不同的方式回到运行态

sleep()——方法中的参数为睡眠时间,时间一到,线程即为可运行的;

suspend()——调用resume()方法来返回;

wait()——该条件变量所在的对象调用notify()notifyAll()方法;

I/O流中发生线程阻塞——待特定的I/O指令结束,自动回到可运行态。

 

死亡态

时机:对线程执行stop()方法,或线程运行结束,即进入死亡态。

说明:可用线程对象=null;代替线程对象.stop();

 

多线程程序设计

Thread继承类创建线程

程序框架

class NewThread extends Thread{ //对应一个任务

  ……

  public void run(){

 

  }

}

  ……

class ThreadTest{

  public static void main(String[] aa){

     NewThread t=new NewThread();

     t.start();

  ……

  }

}

要点:

²       定义一个继承Thread的子类,重写 run()方法,实现一个子任务。

²       main方法中,实例化子类,创建一个线程对象,并调用start()方法执行子任务。

 

例:阅读和分析程序。

class ThreadTest extends Thread{

  int n=0;

  int count=0;

  ThreadTest(int i){

     count=i;

     start();

  }

  public void run(){

     while(true){

        System.out.print(count+" ");

        if(n++>15)return;

//       yield();

     }

  }

  public static void main(String[] aa){

     for(int i=0;i<5;i++)

        new ThreadTest(i);

  }

}

 

得到结论:

²       主线程的执行位置就是main

²       每个线程执行其代码的方式都是顺序执行的。

²       一个线程执行其代码与其他线程相互独立。

 

进一步的理解:

²       线程对于cpu时间片的交替占有特性。

²       yield()方法,让当前线程作出让步。

public static void yield()

²       sleep()可让线程休眠一定的时刻毫秒数

public static void sleep(long millis)

分析和回答

 

1.    程序启动了多少个线程?

2.    线程的执行机会是顺序均匀的吗?

3.    让线程作出让步的方法是?

4.    让线程休眠的方法是?

5.    哪条语句决定一个线程运行结束?

6.    ThreadTest的构造方法中执行start意在?

 

Runnable接口创建线程

Runnable接口继承自Thread,其中只有一个run()方法。

察看Thread的其他构造方法

public  Thread(Runnable target)

public  Thread(Runnable target,String name)

通过Runnable创建线程的步骤:

²       定义一个实现了Runnable的类,其中重写run方法说明任务。

²       通过new Thread(Runnable子类的实例对象)创建线程。

 

练习1:利用Runable改写上例。

 

练习2:用多线程模拟多处卖票的问题(假定总共有100张票,有三个卖票窗口)。

class SellTicket implements Runnable{

  static int num=1;

  public void run(){

 

  }

  public static void main(String[] aa){

 

  }

}

比较:多线程设计时,Runnable接口更具灵活性。一个用户类可以在继承别的类的基础上实现该接口。

思考

1.    程序启动了多少个线程?

2.    线程的执行机会是顺序均匀的吗?

3.    让线程作出让步的方法是?

4.    让线程休眠的方法是?

5.    哪条语句决定一个线程运行结束?

6.    ThreadTest的构造方法中执行start意在?

 

多线程应用举例

一个时钟Applet示例

设计如下图示的时钟,可以逐秒更新显示。

 

设计分析

Clock继承JApplet,并实现Runnable接口,通过一个线程来实现时间的更新显示。

其中各方法的主要任务:

init()——

paint(Graphics g) ——获取当前日历,显示时间

run()——线程的运行(隔一秒刷新)

 

取得系统时间的方法:

Calendar now = Calendar.getInstance();

利用其中的get方法获取HOURMINUNTSECOND

import java.awt.*;

import java.util.*;

import javax.swing.*;

 

public class Click extends JApplet implements Runnable{

  Font f=new Font("",0,50);

  public void init(){

  //创建并运行线程

  }

  public void paint(Graphics g){

     g.clearRect(0,0,this.getWidth(),this.getHeight());

     g.setFont(f);

     g.setColor(Color.green);

     g.drawString(getTime(),50,50);

  }

  public void run(){

  //线程的运行(隔一秒刷新)

  }

  public String getTime(){

     Calendar now=Calendar.getInstance();

     //取得系统时间

  }

}

 

练习:将上例改写为Java Application

 

一个飞行动画Applet

飞船图片文件名:Rocketship.gif

 

线程的优先级和线程调度

CPU的计算机在执行多线程程序时需进行线程调度。

线程优先级的设置

public final void setPriority(int newPriority)

参数:可选线程中的三个静态变量

²       MAX_PRIORITY(高优先级)10

²       MIN_PRIORITY(低优先级)1

²       NORM_PRIORITY(缺省的优先级)5

Java采用的是抢占式的调度方式,高优先级的线程进入可运行(runnable)状态时,会抢占低优先级的线程的位置。

 

例:观察下列程序的执行情况,回答问题。

class ThreadTest{

  public static void main(String[] aa){

     MyThread mt1=new MyThread();

     MyThread mt2=new MyThread();

     mt1.setPriority(5);

     mt2.setPriority(10);

     mt1.start();

     mt2.start();

     while(true){

        System.out.println(Thread.currentThread().getName());

     }    

  }

}

class MyThread extends Thread{

  public void run(){

     while(true){

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

     }    

  }

}

问题

高优先级线程会绝对占据CPU资源吗?

观察程序运行结果,体会线程优先级的意义

 

线程的同步

以上所创建和运行的线程都是独立的,而且异步执行:

²       每个线程自含了运行所需要的数据或方法,不需要外部的资源或方法

²       每个线程不关心其它线程的状态或行为。

 

为什么要同步?

问题:有两个线程thread1thread2,当它们操作同一个对象时,会发现由于thread1thread2是同时执行的,因此可能thread1修改了数据而thread2读出的仍为旧数据。

问题原因:资源使用协调不当(不同步)造成的。

解决方法:协调线程,以免共享资源时,线程间发生冲突。——同步

同步的实现方法

Java提供了同步方法和同步状态来协调资源:

²       同步方法指方法前修饰synchronized关键字

²       同步状态指对象或数据前修饰synchronized关键字

²       被宣布为同步的方法、对象或类数据,在任一时刻只能被一个线程使用

 

同步方法

同步方法:用synchronized修饰的方法

作用:同步方法当一个线程执行时自动加锁,直到方法运行结束锁解除。

例:阅读程序,理解同步方法

class SynchTest implements Runnable{

  public static void main(String args[]){

     SynchTest st=new SynchTest();

     Thread t1=new Thread(st);

     Thread t2=new Thread(st);

     t1.start();

     t2.start();

     while((t1.isAlive())||(t2.isAlive()));

     System.out.println("The test is end.");

  }

  public void run(){

     String name=Thread.currentThread().getName();

     show(name);

     System.out.println(name+" is dead!");

  }

  private  synchronized  void show(String name){

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

        System.out.println("I am "+name+"-- I have run "+j+" times.");

     }

  }

}

 

关键字synchronized会告诉JVM在方法运行时需要一个锁,然后 JVM会创建并管理锁:

²       线程可自动请求锁

²       除非某个线程已经申请并获准使用锁,否则很容易申请到锁

²       当同步方法结束后会自动释放锁

 

同步块

同步块:用synchronized(Object)标识的语句块。

作用:语句块被一个线程执行时会自动加锁,直到运行结束,锁解除。

内容概要:本文档详细介绍了在三台CentOS 7服务器(IP地址分别为192.168.0.157、192.168.0.158和192.168.0.159)上安装和配置Hadoop、Flink及其他大数据组件(如Hive、MySQL、Sqoop、Kafka、Zookeeper、HBase、Spark、Scala)的具体步骤。首先,文档说明了环境准备,包括配置主机名映射、SSH免密登录、JDK安装等。接着,详细描述了Hadoop集群的安装配置,包括SSH免密登录、JDK配置、Hadoop环境变量设置、HDFS和YARN配置文件修改、集群启动与测试。随后,依次介绍了MySQL、Hive、Sqoop、Kafka、Zookeeper、HBase、Spark、Scala和Flink的安装配置过程,包括解压、环境变量配置、配置文件修改、服务启动等关键步骤。最后,文档提供了每个组件的基本测试方法,确保安装成功。 适合人群:具备一定Linux基础和大数据组件基础知识的运维人员、大数据开发工程师以及系统管理员。 使用场景及目标:①为大数据平台建提供详细的安装指南,确保各组件能够顺利安装和配置;②帮助技术人员快速掌握Hadoop、Flink等大数据组件的安装与配置,提升工作效率;③适用于企业级大数据平台的建与维护,确保集群稳定运行。 其他说明:本文档不仅提供了详细的安装步骤,还涵盖了常见的配置项解释和故障排查建议。建议读者在安装过程中仔细阅读每一步骤,并根据实际情况调整配置参数。此外,文档中的命令和配置文件路径均为示例,实际操作时需根据具体环境进行适当修改。
在无线通信领域,天线阵列设计对于信号传播方向和覆盖范围的优化至关重要。本题要求设计一个广播电台的天线布局,形成特定的水平面波瓣图,即在东北方向实现最大辐射强度,在正东到正北的90°范围内辐射衰减最小且无零点;而在其余270°范围内允许出现零点,且正西和西南方向必须为零。为此,设计了一个由4个铅垂铁塔组成的阵列,各铁塔上的电流幅度相等,相位关系可自由调整,几何布置和间距不受限制。设计过程如下: 第一步:构建初级波瓣图 选取南北方向上的两个点源,间距为0.2λ(λ为电磁波波长),形成一个端射阵。通过调整相位差,使正南方向的辐射为零,计算得到初始相位差δ=252°。为了满足西南方向零辐射的要求,整体相位再偏移45°,得到初级波瓣图的表达式为E1=cos(36°cos(φ+45°)+126°)。 第二步:构建次级波瓣图 再选取一个点源位于正北方向,另一个点源位于西南方向,间距为0.4λ。调整相位差使西南方向的辐射为零,计算得到相位差δ=280°。同样整体偏移45°,得到次级波瓣图的表达式为E2=cos(72°cos(φ+45°)+140°)。 最终组合: 将初级波瓣图E1和次级波瓣图E2相乘,得到总阵的波瓣图E=E1×E2=cos(36°cos(φ+45°)+126°)×cos(72°cos(φ+45°)+140°)。通过编程实现计算并绘制波瓣图,可以看到三个阶段的波瓣图分别对应初级波瓣、次级波瓣和总波瓣,最终得到满足广播电台需求的总波瓣图。实验代码使用MATLAB编写,利用polar函数在极坐标下绘制波瓣图,并通过subplot分块显示不同阶段的波瓣图。这种设计方法体现了天线阵列设计的基本原理,即通过调整天线间的相对位置和相位关系,控制电磁波的辐射方向和强度,以满足特定的覆盖需求。这种设计在雷达、卫星通信和移动通信基站等无线通信系统中得到了广泛应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值