Java线程编程基础 第一章
1、本章任务:建立多线程程序
2、本章知识点:
- 了解Java线程模型
- 掌握创建线程的方法
- 设置线程优先级
3、多线程编程概述
- Java提供了对多线程编程的内置支持
- 多线程程序包括能够并发运行的两个或多个代码段
单线程、多线程对比示例图:
从图中可以看出:
1.单线程中的3段代码只能在不同的时间点执行,
2.多线程中的3段代码可以在同一时间点执行
4、进程与线程
- 进程:一个进程是 一个正在执行的程序 ,目前的操作系统大多都支持同时运行两个或多个程序,称为多任务操作系统
- 线程:单个程序 可以同时执行 两个或更多的任务 ,每个任务称为 一个线程 ,进程包含线程
- 进程是重量级的任务 ,每个进程都有自己独立的地址空间
- 线程是轻量级的任务 ,他们共享一个地址空间,共享同一个进程
- 进程间通信代价昂贵而且受限,线程通信很容易
- 利用多线程可使你的程序最大限度地利用CPU,因为空闲时间被限制在最小
5、 Thread类和Runnable接口
Java的多线程系统构建在Thread类 、方法以及Runnable接口 之上。
可以通过继承Thread类或者实现Runnable接口创建一个新线程
Thread类定义了几个管理线程的方法,常用方法如下:
方法名 功能
====================================
getName 获取线程名
getPriority 获得线程的优先级
isAlive 判定线程是否仍在运行
join 等待一个线程终止
run 线程入口方法
sleep 暂停一个线程一段时间
start 启动线程
6、程序的主线程
6.1)主线程描述
Java程序启动时 (从main方法开始),一个线程 立刻开始运行,这个线程称
为主线程 ,主线程的重要性体现在如下两个方面:
1)它是产生其他子线程的线程
2)一般情况下,必须是最后一个结束 执行的线程,
因为它要执行各种关闭操作
主线程不但在程序开始时自动创建,也能通过Thread对象来控制,此时需要
调用:Thread.currentThread()
6.2)主线程控制示例代码
public static void main(String args[ ]){
Thread t = Thread.currentThread( ); //获取当前线程(主线程)
System.out.println("当前线程:" + t); //输出:线程名-优先级-线程组名
//改变主线程名称
t.setName("My Thread"); System.out.println("改变之后的名称:" + t);
//打印数字1 ~ 5,每隔1秒打印1次
try{
for(int i = 1; i <= 5; i++){
System.out.println(i);
Thread.sleep(1000); //休眠1000毫秒
}
}catch(InterruptedException e){
System.out.println( " 主线程中断! " );
}
}
7、创建线程
Java定义了两种 创建线程的方法 :
1)可以实现Runnable接口 ,这是创建线程最简单的方法,实现Runnable接口
只需一个简单的run()方法,其声明如下:
public void run();
run()方法是线程的进入点,线程在run方法返回时结束
2)可以继承Thread类 ,重写Thread类的run()方法
8、创建线程 方法1:实现Runnable接口
/**
* 线程类MyThread,实现Runnable接口
*/
public class MyThread implements Runnable{ //线程类
//线程入口点
public void run(){
try{
//打印数字1 ~ 5,每隔500毫秒打印一次
for(int i = 1; i <= 5; i++){
System.out.println("子线程打印:" + i);
Thread.sleep(500); //休眠500毫秒
}
}catch(InterruptedException e){
System.out.println("子线程中断!");
}
System.out.println("子线程执行结束!");
}
}
/**
* 线程类MyThread的测试类
*/
public class TestMyThread{
public static void main(String args[ ]){
//创建线程
Thread t = new Thread(new MyThread(), "Demo Thread");
System.out.println("子线程被创建:" + t); //显示线程信息
t.start(); //启动线程
//主线程中间隔1秒,打印数字1 ~ 5
try{
for(int i = 1; i <= 5; i++){
System.out.println("主线程打印:" + i);
Thread.sleep(1000); //休眠1000毫秒
}
}catch(InterruptedException e){
System.out.println("主线程中断!");
}
System.out.println("主线程执行结束!");
}
}
9、创建线程 方法2:继承Thread类
/**
* 线程类MyThread1,继承Thread类
*/
public class MyThread1 extends Thread{
public MyThread1(){
super("Demo Thread"); //线程命名
}
//重写线程入口方法
public void run(){
try{
//打印数字1 ~ 5,每隔500毫秒打印一次
for(int i = 1; i <= 5; i++){
System.out.println("子线程打印:" + i);
Thread.sleep(500); //休眠500毫秒
}
}catch(InterruptedException e){
System.out.println("子线程中断!");
}
System.out.println("子线程执行结束!");
}
}
/**
* 线程类MyThread1的测试类
*/
public class TestMyThread1{
public static void main(String args[ ]){
//创建线程
Thread t = new MyThread1();
System.out.println("子线程被创建:" + t); //显示线程信息
t.start(); //启动线程
//主线程中间隔1秒,打印数字1 ~ 5
try{
for(int i = 1; i <= 5; i++){
System.out.println("主线程打印:" + i);
Thread.sleep(1000); //休眠1000毫秒
}
}catch(InterruptedException e){
System.out.println("主线程中断!");
}
System.out.println("主线程执行结束!");
}
}
10、如何选择创建线程的方式
问: Java有两种创建线程的方式,哪种更好?
1. Thread类定义了多个派生类可以重写的方法,run()方法只是其中之一,
所以只有在需要增强或者修改Thread类时才应该使用继承Thread类方式
2. 如果不想重写Thread类的run()方法外的其他方法,最好还是简单的
实现Runnable接口(推荐)
11、线程的状态
- 准备运行状态:ready to run
- 运行状态:running
- 暂停状态:suspended 运行中的线程可以暂停,暂停的线程可以重新启动
- 阻塞状态:blocked 线程在等待其他资源时被阻塞
(这个状态和视频播放器的操作非常类似,: )
注意:线程在任何时候都能被终止,一旦终止就不能被重新激活
12、启动多个线程
/**
* 改进的MyThread线程类(改为自启动线程)
*/
public class MyThread implements Runnable {
private Thread t;//线程对象
public Thread getT() {
return t;
}
/**构造方法*/
public MyThread(String threadName){
//创建线程对象
this.t = new Thread(this, threadName);
System.out.println("创建子线程:" + threadName);
//启动线程
this.t.start();
}
/**
* 功能:线程入口方法
*/
public void run() {
Thread t = Thread.currentThread();
try {
//打印数字1 ~ 5,间隔1秒
for(int i = 1; i <= 5; i++){
System.out.println(t.getName() + " 打印:" + i);
//休眠1秒
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("子线程中断!");
}
System.out.println("子线程结束!");
}
}
/**
* 多线程测试类
*/
public class TestMyThread {
public static void main(String[] args) {
//创建线程对象1
MyThread t1 = new MyThread("子线程1");
//创建线程对象2
MyThread t2 = new MyThread("子线程2");
try {
//主线程打印数字1 ~ 5,间隔1秒
for(int i = 1; i <= 5; i++){
System.out.println("主线程打印:" + i);
//休眠1秒
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("主线程中断!");
}
System.out.println("主线程结束!");
}
}
13、使用isAlive() 和join()
问题: 主线程一般要最后结束,之前的示例中是通过在main方法中加入sleep()
使主线程休眠足够长的时间以确保主线程最后结束,这个解决方式合理吗?怎样
才能知道子线程是否终止?或者说怎样才能保证主线程最后结束?
答案: 有两种方式可以确定一个线程是否结束:
1)在线程中调用isAlive(),这个方法由Thread定义,如果它调用的线程仍在运行,
返回true,否则返回false
2)使用join()方法来等待另一个线程的结束,该方法一直等待直到它调用的线程终止
14、isAlive()和jion()示例
尝试运行以下代码并观察主线程是否最后结束:
public static void main(String[] args) {
MyThread t1 = new MyThread("子线程1"); // 创建线程1
MyThread t2 = new MyThread("子线程2"); // 创建线程2
MyThread t3 = new MyThread("子线程3"); // 创建线程3
//显示线程是否运行
System.out.println("子线程1是否运行:" + t1.getT().isAlive());
System.out.println("子线程2是否运行:" + t2.getT().isAlive());
System.out.println("子线程3是否运行:" + t3.getT().isAlive());
// 主线程等待子线程结束
try {
System.out.println("等待子线程结束…");
t1.getT().join(); // 等待t1线程结束
t2.getT().join(); // 等待t2线程结束
t3.getT().join(); // 等待t3线程结束
} catch (InterruptedException e) {
System.out.println("主线程中断!");
}
//显示线程是否运行
System.out.println("子线程1是否运行:" + t1.getT().isAlive());
System.out.println("子线程2是否运行:" + t2.getT().isAlive());
System.out.println("子线程3是否运行:" + t3.getT().isAlive());
System.out.println("主线程执行结束!");
}
15、线程优先级概述
Java给每个线程分配一个优先级,以决定哪个线程可以优先分配CPU时间
优先级是一个整数,用于指定线程的相对优先程度
优先级可以决定什么时候从一个运行中的线程切换到另一个线程,切换规则如下:
- 一个线程自愿释放控制(放弃、睡眠或者阻塞),所有其他线
程被检查,高优先级线程被分配CPU时间 - 一个线程可以被另一个高优先级线程抢占资源,称为抢占式多任务
注意:具有同一优先级的线程竞争CPU时间,不同操作系统的处理方式上存在差别
16、线程优先级设置
- 线程调度器使用线程优先级以决定什么时候允许运行
- 理论上高优先级的线程将比低优先级的线程得到更多CPU时间
- 低优先级线程在运行时,高优先级的线程会抢占低优先级线程的执行权
- 设置线程优先级,使用setPriority()方法:
final void setPriority(int level);
参数level为线程的优先级 ,其值在1 ~ 10 之间
Thread类提供了如下常量方便优先级的设置
Thread.MIN_PRIORITY == 1
Thread.MAX_PRIORITY == 10
Thread.NORM_PRIORITY == 5
17、线程优先级示例
/**
* 功能:计数器线程
*/
public class Clicker implements Runnable {
private int click = 0;//计数值
private Thread t;//线程对象
private volatile boolean running = true;//运行开关
public int getClick() {
return click;
}
public Thread getT() {
return t;
}
/**构造方法,参数为优先级*/
public Clicker(int priority){
this.t = new Thread(this);
this.t.setPriority(priority);//设置线程优先级
}
public void run() {
while(this.running){
this.click++;//计数器累加
}
}
public void start(){
this.t.start();//启动线程
}
public void stop(){
this.running = false;//结束线程
}
}
/**
* 计数器线程测试类
*/
public class TestClicker {
public static void main(String[] args) {
//创建线程,设置4种档次的优先级
Clicker hi = new Clicker(Thread.NORM_PRIORITY + 2);
Clicker hi1 = new Clicker(Thread.NORM_PRIORITY + 4);
Clicker lo = new Clicker(Thread.NORM_PRIORITY - 2);
Clicker lo1 = new Clicker(Thread.NORM_PRIORITY - 4);
//启动线程
hi.start();
hi1.start();
lo.start();
lo1.start();
try {
Thread.sleep(2000);//休眠2秒
//结束线程
hi.stop();
hi1.stop();
lo.stop();
lo1.stop();
//等待线程结束
hi.getT().join();
hi1.getT().join();
lo.getT().join();
lo1.getT().join();
} catch (InterruptedException e) {
System.out.println("主线程中断!");
}
//显示计数器值
System.out.println("lo1计数器:" + lo1.getClick());
System.out.println("lo计数器:" + lo.getClick());
System.out.println("hi计数器:" + hi.getClick());
System.out.println("hi1计数器:" + hi1.getClick());
System.out.println("主线程结束!");
}
}
18、总结
- 进程与线程的区别?
- 如何获取主线程?
- 创建线程的两种方式?
- 获知线程是否运行使用哪个方法?
- 等待线程执行结束使用哪个方法?
- 线程优先级的范围是什么?