Java高级应用编程 —— 多线程编程

多线程实现的两种方式

(1) 创建线程类

  • 继承Thread类
  • 或 实现Runnable接口

(2) 通过Thread类构造器来创建线程对象

  • Thread( )
  • Thread(Runnable target)
    (3) 通过start()方法激活线程对象

• run( )方法 — 线程运行体

要将一段代码(线程体)在一个新的线程上运行,该代码应该在一 个线程类的run( )函数中

  • 写一个类implements Runnable接口,且必须覆盖Runnable接口 中的run( )方法
  • 写一个类extends Thread类,且必须重写Thread类的run( )方法

run( ) 与 start( )的区别

1、start()方法
用start方法来启动线程,是真正实现了多线程,通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法。但要注意的是,此时无需等待run()方法执行完毕,即可继续执行下面的代码。所以run()方法并没有实现多线程。
2、run()方法
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码。
在这里插入图片描述

继承Thread类和实现Runnable接口两种方式的比较

  • 使用Runnable接口可以避免由于JAVA的单继承性带来的局限
  • 适合多个相同的程序代码的线程去处理同一资源情况,把线程同 程序的代码、数据有效的分离(推荐使用实现Runnable接口)

线程状态及其生命周期

一个 Thread 对象在它的整个生存期中能以几种不同的状态存在
在这里插入图片描述
start( ) — 方法使线程处于可以运行的状态,但 不一定意味着该线程立即开始运行

线程常用方法

方法功能
isAlive()判断线程是否还活着
getPriority()获取线程的优先级
setPriority()设置线程的优先级
Thread.sleep()将当前线程睡眠指定毫秒数
jion()调用某线程的该方法,将当前线程与该线程合并,即等待该线程结束,再恢复当前线程的运行
yield()让出cpu,当前线程进入就绪队列等待调度

线程的优先级

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有 线程,线程调度器按照线程的优先级来决定应调度哪个线程来执行
  • Java线程的优先级用1~10的整数来表示,越小则优先级越低
  • 但是Java的优先级是高度依赖于操作系统的实现的

Thread类的三个常量,表示常用的线程优先级:
     - Thread.MIN_PRIORITY //1
     - Thread.NORM_PRIORITY // 5
     - Thread.MAX_PRIORITY // 10

线程优先级Demo

package com.neuedu.chapter03._多线程_常用方法;

import com.neuedu.chapter03._多线程.RunnableDemo;
public class ThreadState {
	public static void main(String[] args) {
		Thread t1 = new Thread(new RunnableDemo("线程1"));
		Thread t2 = new Thread(new RunnableDemo("线程2"));
		Thread t3 = new Thread(new RunnableDemo("线程3"));
		
		//打印优先级数
		System.out.println("t1优先级数(默认值): "+t1.getPriority());
		System.out.println("t2优先级数(默认值): "+t2.getPriority());
		//System.out.println("t3优先级数(默认值): "+t3.getPriority());
		
		//设置优先级
		t1.setPriority(Thread.MIN_PRIORITY);
		t2.setPriority(Thread.MAX_PRIORITY);
		//t3.setPriority(Thread.MIN_PRIORITY);
		
		//打印优先级数
		System.out.println("t1优先级数(默认值): "+t1.getPriority());
		System.out.println("t2优先级数(默认值): "+t2.getPriority());
		System.out.println("t3优先级数(默认值): "+t3.getPriority());
		//启动线程
		t1.start();
		t2.start();
		//t3.start();
	}
}

运行结果:
在这里插入图片描述

线程的休眠

sleep( )
  • 让线程中止一段时间的静态方法
  • Thread.sleep(long millis) — 暂时停止执行millis毫秒
  • 在睡眠期满的瞬间,再次调用该线程不一定会恢复它的执行

Demo如下:

package com.neuedu.chapter03._多线程_常用方法;

import java.text.SimpleDateFormat;
import java.util.Date;
class DateThread extends Thread{
	public void run() {
		while(true) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
			System.out.println("北京时间"+sdf.format(new Date()));
			try {
				Thread.sleep(3000);  //参数已毫秒为单位,(此处表示让线程暂停3秒)
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
public class SleepDemo {
	public static void main(String[] args) {
		DateThread t1 = new DateThread();
		t1.start(); //启动线程
	}
}
join()

导致当前线程等待,直到调用这个 join 方法的线程终止 • join( ) join(long millis) join(long millis,int nanos)

Demo如下:

package com.neuedu.chapter03._多线程_常用方法;

public class jionDemo {
	public static void main(String[] args) {
		//线程1
		Thread3 t1 = new Thread3(6,10);
		t1.start();
		try {
			t1.join();  //终止线程
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
		//线程2
		Thread3 t2 = new Thread3(20,30);
		t2.start();
		for(int i = 1;i<5;i++) {
			System.out.println(Thread.currentThread().getName()+" : "+i);
		}
	}
}
class Thread3 extends Thread{
	private int x;
	private int y;
	
	public Thread3(int x,int y) {
		this.x = x;
		this.y = y;	
	}
	public void run() {  //重写run()方法
		for(int i=x;i<y;i++) {
			System.out.println(Thread.currentThread().getName()+" : "+i);
		}
	}
}
yield()
  • 为其他可运行的线程提供执行机会
  • 静态方法 — Thread.yield( )
package com.neuedu.chapter03._多线程_常用方法;
public class yielDemo {
	public static void main(String[] args) {
		
		
		Thread5 runnable2 = new Thread5();
		/*
		 * 通过实现Runnable接口实现的类只是一个方法,需要将它转化为Thread对象才能使用start()方法
		 * 这也是继承Thread类和实现Runnable两种方法在语法上的区别
		 */
		Thread t1 = new Thread(runnable2);  
				
		Thread4 runnable = new Thread4();
		Thread t2 = new Thread(runnable);
		
		t1.start();
		t1.yield();
		t2.start();
		//t2.yield();
	}
}
class Thread4 implements Runnable{  //实现Runable接口的方法实现多线程
		
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<50;i++) {
			if(i<50) {
				Thread.yield();
				System.out.println(Thread.currentThread().getName()+" : "+"执行了"+(i+1)+"次");
			}else {
				System.out.println(Thread.currentThread().getName()+"------------执行完毕------------");
			}
		}
	}		
}
class Thread5 implements Runnable{
		
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i<=50;i++) {
			if(i<50) {				
				System.out.println(Thread.currentThread().getName()+" : "+"谦让了第"+(i+1)+"次");
				
			}else {
				System.out.println(Thread.currentThread().getName()+"------------执行完毕------------");
			}
		}
	}		
}

线程同步

有时两个或多个线程可能会试图同时访问一个资源
例如,一个线程可能尝试从一个文件中读取数据,而另一个线程 则尝试在同一文件中修改数据 在此情况下,数据可能会变得不一致

为了确保在任何时间点一个共享的资源只被一个线程 使用,使用了“同步”

  • 当一个线程运行到需要同步的语句后,CPU不去执行其他线程中的、 可能影响当前线程中的下一句代码的执行结果的代码块,必须等 到下一句执行完后才能去执行其他线程中的相关代码块,这就是 线程同步.

实现同步的两种方式及其优缺点

1、synchronized方法
优点:

  • 可以显示的知道哪些方法是 被synchronized关键字保 护的

缺点:

  • 方法中有些内容是不需要同步 的,如果该方法执行会花很长 时间,那么其他人就要花较多 时间等待锁被归还;
  • 只能取得自己对象的锁,有时 候程序设计的需求,可能会需 要取得其他对象的锁;

2、synchronized代码块
优点:

  • 可以针对某段程序代码同步, 不需要浪费时间在别的程序代 码上;
  • 可以取得不同对象的锁;

缺点:

  • 无法显示的得知哪些方法 是被synchronized关键字 保护的;

synchronized()代码块的Demo
以两个售票员同时买票的例子为例:
写一个事项Runnable接口的类

package com.neuedu.chapter03._同步;

public class TicketRunnable implements Runnable{
	private int num = 50;  //票数
	@Override
	public void run() {  //票数大于零,运行代码
		// TODO Auto-generated method stub
		while(num >0) {  
			synchronized(this) {  //需要同步的代码块
				System.out.println("售票员:  "+Thread.currentThread().getName()+"售出了票号"+num+"的票");
				num--;  //打印一次票数减 1
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

创建线程

package com.neuedu.chapter03._同步;

public class TestTicketRunnable {
	public static void main(String[] args) {
		TicketRunnable runnable = new TicketRunnable();
		Thread t1 = new Thread(runnable);  //创建线程1
		Thread t2 = new Thread(runnable);  //创建线程2
		t1.start();
		t2.start();
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 《多线程编程实战第二版》是一本经典的多线程编程书籍,全书共分为14章,内容详实,完整涵盖了多线程编程的各个方面。 本书第一章介绍了多线程编程的背景和概念,并讨论了多线程编程的优势和缺点。第二章介绍了多线程编程中最重要的概念——线程。通过对线程的创建、启动、挂起和停止等操作的详细介绍,为后续开发做好了铺垫。 第三章和第四章分别介绍了线程的同步和互斥技术。这两章内容非常重要,因为多线程编程中,线程之间的合作和竞争是非常常见的情况。掌握了同步和互斥技术,才能编写出高质量的多线程程序。 第五章至第七章介绍了线程池的原理和实现。线程池是一种重要的线程管理技术,可以提高多线程程序的可维护性和可靠性。 第八章至第十二章介绍了多线程编程中的高级技术,例如线程调度、异常处理、锁协议和信号量等技术。这些技术对于编写高质量的多线程程序非常有帮助。 第十三章介绍了多线程编程中的常见问题和错误,并提供了解决方法。这些问题和错误非常实用,可以帮助程序员避免一些常见的多线程编程错误。 最后,第十四章提供了编写高质量多线程程序的一些最佳实践和指导。这些实践和指导非常有价值,可以提高程序员的编程水平。 总而言之,《多线程编程实战第二版》是一本非常优秀的多线程编程书籍,适合所有对多线程编程感兴趣的程序员学习。 ### 回答2: 《多线程编程实战第二版》是一本关于Java多线程编程的经典书籍,该书全面深入地介绍了Java多线程编程的各个方面。读者可以通过阅读本书学习到多线程编程的基本概念、多线程的并发性和同步性、线程池、线程协作等内容。 本书的重点是实战。作者通过丰富的代码示例和实际案例来展示Java多线程编程在实际项目中的应用。例如,作者为读者介绍了如何使用线程池来提高程序的性能和效率,如何通过线程协作来避免线程之间的竞争和冲突。 此外,该书还深入介绍了Java 5引入的Lock和Condition机制,以及Java 8中引入的CompletableFuture和Stream API。这些新特性为多线程编程带来了新的思路和新的解决方案。读者可以通过本书的学习深入理解并掌握这些新特性的使用方式。 总之,《多线程编程实战第二版》是一本Java多线程编程的必备参考书籍。它不仅介绍了多线程编程的基础知识和经典案例,还提供了实战经验和高级技巧。无论你是初学者还是有经验的开发者,都可以从中受益。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

code_mo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值