多线程
发布日期:2021-05-10 03:44:19 浏览次数:25 分类:精选文章

本文共 3521 字,大约阅读时间需要 11 分钟。

Java多线程编程详解

1. 进程与线程

在计算机科学中,进程和线程是并发编程的基础概念。一个进程可以看作是一个执行应用程序的实例,它拥有一定的内存空间和系统资源。线程则是在进程中由操作系统调度和分派的基本单位,负责执行应用程序的具体任务。线程是CPU调度的基本单位,负责完成特定的顺序控制流程。

线程的特点

  • 独立执行:线程是进程中的最小执行单位,能够独立执行任务。
  • 资源共享:线程共享进程的内存和系统资源。
  • 并发执行:线程之间可以交替执行,提高资源利用率。

进程与线程的区别

  • 资源占用:进程占用更多的系统资源,包括内存、文件描述符等。
  • 启动方式:进程通常由操作系统启动,而线程可以在进程内部动态创建。
  • 调度粒度:进程的调度粒度较大,线程的调度粒度较小。

2. 多线程编程

多线程的优势

  • 资源利用:充分利用CPU资源,减少等待时间。
  • 任务并行:实现多任务同时执行,提升效率。
  • 用户体验:减少用户等待时间,提升应用响应速度。

多线程的实现方式

  • 继承Thread类:通过继承Thread类,重写run方法实现线程。
  • 实现Runnable接口:通过实现Runnable接口,使用Thread类执行Runnable实例。

Thread类与线程管理

  • 主线程:main方法即为主线程,负责程序的入口。
  • 子线程:通过start方法启动其他线程。
  • 线程状态:线程可以处于新建、就绪、运行、阻塞、等待、终止状态。

3. 线程创建方式对比

继承Thread类

  • 代码示例:
public class MyThread extends Thread {
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "第" + i + "次执行");
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start();
t2.start();
}
}

优点:直接操作线程,适用于单继承。

实现Runnable接口

  • 代码示例:
public class MyRunnable implements Runnable {
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "第" + i + "次执行");
}
}
public static void main(String[] args) {
Runnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable, "线程1");
Thread t2 = new Thread(myRunnable, "线程2");
t1.start();
t2.start();
}
}

优点:避免单继承的局限性,适合多继承场景。

4. 线程的状态与调度

线程状态

线程在执行过程中会经历多个状态:

  • 新建状态:线程创建时的初始状态。
  • 就绪状态:线程准备好执行的状态。
  • 运行状态:线程正在CPU上执行任务的状态。
  • 阻塞状态:线程等待I/O操作完成的状态。
  • 等待状态:线程等待其他线程通知的状态。
  • 终止状态:线程正常结束的状态。

线程调度

线程调度是操作系统将线程分配CPU的过程。使用Thread类中的方法可以对线程进行调度:

  • start():启动线程执行。
  • join():等待线程完成。
  • wait():让线程等待。
  • notify():唤醒等待的线程。
  • yield():让当前线程暂停,供其他线程执行。

线程优先级

线程优先级由1到10表示,1为最低优先级,10为最高优先级。优先级高的线程获得CPU资源的概率较大。

5. 线程的同步与锁

同步代码块

synchronized代码块可以保护共享资源的访问,防止数据竞争和 race condition。使用synchronized块可以确保在多线程环境下,共享资源的访问是安全的。

死锁

死锁是指多个线程在等待对方完成的操作而无法继续执行的状态。发生死锁的原因是线程尝试占有多个锁,而这些锁之间没有相互释放的机制。

解决死锁

  • 减少锁嵌套:尽量减少对多个锁的嵌套使用。
  • 释放锁:确保当前线程在等待时释放锁。
  • 使用非阻塞锁:如ReentrantLock,提供更高的灵活性。

6. 生产者与消费者问题

问题描述

生产者不断生产商品,消费者不断消费商品。如何在高并发环境下确保生产者和消费者的高效交替,防止数据丢失和不一致。

解决方案

  • 使用synchronized方法控制对共享资源的访问。
  • 使用wait和notify方法实现生产者与消费者的等待和通知。
  • 使用ThreadLocal来保护共享资源的局部性。

代码示例

public class Movie {
private String name;
private String info;
private Boolean flag = true;
public String getName() {
return name;
}
public String getInfo() {
return info;
}
public synchronized void get() {
if (flag) {
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(this.getName() + "-" + this.getInfo());
flag = true;
super.notify();
}
public synchronized void set(String name, String info) {
if (!flag) {
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.info = info;
flag = false;
super.notify();
}
}

7. 实际应用中的线程优化

线程礼让

线程礼让可以让当前线程暂停一段时间,允许其他线程获得执行机会。

线程强制执行

在某些场景下,需要强制让线程立即执行当前任务,而不等待I/O操作完成。

线程阻止与恢复

使用Thread类的stop和resume方法,可以对线程进行阻止和恢复操作。

8. 总结

多线程编程能够显著提升应用程序的性能,但也带来了较高的复杂性。合理使用线程池、同步机制和并发控制,可以有效避免线程安全问题,提升应用程序的稳定性和性能。

上一篇:虚拟机、mysql、hadoop伪分布式、高可用集群
下一篇:输入输出流(二)

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2025年04月09日 14时35分08秒