多线程

本文深入探讨了线程的基础概念及其在Java中的应用,包括线程的创建、启动、状态管理和调度策略等内容。此外,还详细介绍了线程同步、死锁及线程间通信的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

线程基础

进程与线程

线程分类

多线程的优势

线程控制

线程的创建和启动

线程的状态

线程调度

线程同步

线程同步的必要性

线程同步的实现

死锁

线程间通信

线程间通信的必要性

线程间通信的实现


程序

程序是一段静态的代码,它是应用程序执行的蓝本

进程

进程是指一种正在运行的程序,有自己的地址空间

进程的特点

动态性

并发性

独立性

线程的定义

进程内部的一个执行单元,它是程序中一个单一的顺序控制流程。如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为多线程

进程是系统资源分配的单元,可包括多个线程

线程是独立调度和分派的基本单位,共享进程资源

引入进程是为了对个线程并发执行,提高资源的利用率和系统吞吐量

引入线程是为了减少程序在并发执行时付出的时空开销

线程分类

系统级线程

由操作系统内核进行管理,使用户程序可以创建、执行、撤销线程

用户级线程

管理过程全部由用户程序完成,操作系统内核只对进程进行管理

多线程的优势

多线程使系统空转时间减少,提高CPU 利用率

进程间不能共享内存,但线程之间共享内存非常容易

使用多线程实现任务并发比多线程的效率高

Java语言内置多线程功能支持,简化了Java的多线程编程


线程的创建和启动

两种方法来创建线程

继承 Java.lang.Thread类,并覆盖run()方法

class MyThread extends Thread {
     public void run( ) {
         /* 覆盖该方法*/
      }
 }

实现Java.lang.Runnable接口,并实现run()方法

class MyThread implements Runnable{
      public void run( ) {
            /* 实现该方法*/ 
      }
 }

启动线程

新建的线程不会自动开始运行,必须通过start()方法启动

启动继承Thread的线程

MyThread  t = new MyThread ();
t.start();

启动实现Runnable接口的线程

MyThread  mt =   new MyThread ();
Thread    t  =   new Thread(mt);
 t.start();

继承 Java.lang.Thread类

代码实现如下:

public class ThreadDemo1 {
	public static void main(String args[]) {
		MyThread1 t = new MyThread1();
		t.start();
		while (true) {
			System.out.println("兔子领先了,别骄傲");
		}
	}
}

class MyThread1 extends Thread {
	public void run() {
		while (true) {
			System.out.println("乌龟领先了,加油");
		}
	}
}

实现Java.lang.Runnable接口

代码实现如下:

public class ThreadDemo2 {
	public static void main(String args[]) {
		MyThread2 mt = new MyThread2();
		Thread t = new Thread(mt);
		t.start();
		while (true) {
			System.out.println("兔子领先了,加油");
		}
	}
}

class MyThread2 implements Runnable {
	public void run() {
		while (true) {
			System.out.println("乌龟超过了,再接再厉");
		}
	}
}

两种线程创建方式的比较

继承Thread类方式的多线程

优势:编写简单

劣势:无法继承其它父类

实现Runnable接口方式的多线程

优势:可以继承其它类,多线程可共享同一个Thread对象

劣势:编程方式稍微复杂,如果需要访问当前线程,需要调用Thread.currentThead()方法


Thread类的常用方法

方法功能
static Thread currentThread() 得到当前线程
final String getName()返回线程的名称
final void setName(String name)将线程的名称设置为name指定的名称
void start()调用run()方法启动线程,开始线程的执行
oid run()存放线程体代码

线程的状态

新生

使用new 关键字创建一个线程后,尚未调用其start方法之前

可运行

调用线程对象的start方法之后

这个状态当中,线程对象可能正在运行,也可能等待运行

阻塞

一种“不可运行”的状态,在得到一个特定的事件之后会返回到可运行状态

死亡

线程的run方法运行完毕或者在运行中出现未捕获的异常时


生命周期:



线程调度

优先级概述

每个线程执行时都具有一定的优先级。当调度线程时,会优先考虑级别高的线程

默认情况下,一个线程继续其父线程的优先级

使用线程对象.setPriority(p)来改变线程的优先级

优先级影响CPU在线程间切换,切换原则是:

当一个线程通过显式放弃、睡眠或者阻塞、自愿释放控制权时,所有线程均接受检查而优先级高线程将会优先执行

一个线程可以被一个高优先级的线程抢占资源

同级别的线程之间,则通过控制权的释放,确保所有的线程均有机会运行

join()

阻塞指定线程等到另一个线程完成以后再继续执行

代码实现如下:

public class JoinTest extends Thread {
	public JoinTest(String name) {
		super(name);
	}
	public void run() {
		for (int i = 0; i < 5; i++) 
			System.out.println(getName() + "" + i);
	}
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			if (i == 5) {
				JoinTest tempjt = new JoinTest("半路加入的线程");
				try {
					tempjt.start();
					tempjt.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName()+""+i);
		}
	}
}

sleep()

使线程停止爱运行一段时间,将处于阻塞状态

阻塞的时间有指定的毫秒数决定

代码实现如下:

public class TestSleep {
	public static void main(String[] args) {
		System.out.println("Wait");
		// 让主线程等待5秒再执行
		Wait.bySec(5);
		// 提示恢复执行
		System.out.println("start");
	}
}
class Wait {
	public static void bySec(long s) {
		// sleep s个1秒
		for (int i = 0; i < s; i++) {
			System.out.println(i + 1 + "秒");
			try {
				// sleep1秒
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}


yield()

让当前正在执行的线程暂停

该方法不会阻塞线程,而是将线程转入可运行状态

代码实现如下:

public class YeildTest {
	public static void main(String[] args) {
		TheThread mt = new TheThread();
		MyNewThread mnt = new MyNewThread();
		mt.start();
		mnt.start();
	}
}
class TheThread extends Thread {
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("第一个线程的第 " + (i + 1) + "次运行");
			Thread.yield();
		}
	}
}
class MyNewThread extends Thread {
	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println("第二个线程的第 " + (i + 1) + "次运行");
			Thread.yield();
		}
	}
}


sleep()和yiel()对比

 sleep()yiel()
暂停后的状态进入被阻塞的状态,直到经过指定时间后,才进入可运行状态直接将当前线程转入可运行状态
没有其他等待运行的线程当前线程会继续等待指定的时间当前线程会马上恢复执行
等待线程的优先级别不考虑,机会均等将优先级相同或更高的线程运行


setDaemon()

可以将指定的线程设置成后台线程

创建后台线程的线程结束时,后台线程也随之消亡

代码实现如下:

public class DaemonTest extends Thread {
	public void run() {
		while (true) {
			System.out.println(getName());
		}
	}
	public static void main(String[] args) {
		DaemonTest dt = new DaemonTest();
		dt.setDaemon(true);
		dt.setName("后台线程");
		dt.start();
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName()+" "+i);
		}
	}
}

线程同步的必要性

当多个线程访问同一个数据时,容易出现线程安全问题。需要让线程同步,保证数据安全

线程同步

当两个或两个以上线程访问同一资源时,需要某种方式来确保资源在某一时刻只被一个线程使用

线程同步的实现方案

同步代码块

同步方法

同步代码块语法:

synchronized(obj){
	//此处代码为同步代码块
}
public class TestAccount implements Runnable {
	// 所有的用此TestAccount对象创建的线程共享同一个线程
	private Account acct = new Account();
	public void run() {	…	}
	private void makeWithdrawal(int amt) {
		synchronized (acct) {
			if (acct.getBalance() >= amt) {
				System.out.println(Thread.currentThread().getName() 
							+ " 准备取款");
				try {
					Thread.sleep(500);
				} catch (InterruptedException ex) {			}
				acct.withdraw(amt); // 如果余额足够,则取款
				System.out.println(Thread.currentThread().getName()
								 + " 完成取款");
			} else {// 余额不够给出提示
				System.out.println("余额不足以支付"+ Thread.currentThread() .getName() + " 的取款,余额为 "+ acct.getBalance());
			}
		}
	}
}

同步方法语法:

访问修饰符 synchronized 返回类型 方法名{

}
public class TestAccount implements Runnable {
	// 所有的用此TestAccount对象创建的线程共享同一个线程
	private Account acct = new Account();
	public void run() {
		for (int x = 0; x < 5; x++) {
			makeWithdrawal(100); // 取款
			if (acct.getBalance() < 0) 	System.out.println("账户透支了!");
		}
	}
	private synchronized void makeWithdrawal(int amt) {
		if (acct.getBalance() >= amt) {
			System.out.println(Thread.currentThread().getName()+"准备取款");
			try {
				Thread.sleep(500);
			} catch (InterruptedException ex) {			}			
			acct.withdraw(amt); // 如果余额足够,则取款
			System.out.println(Thread.currentThread().getName() + " 完成取款");
		} else {// 余额不够给出提示			
			System.out.println("余额不足以支付 " + Thread.currentThread().getName()
					+ " 的取款,余额为 " + acct.getBalance());
		}
	}
}


死锁

线程同步的好处

解决了线程安全问题

线程同步的缺点

性能下降

会带来死锁

死锁

当两个线程相互等待对方释放“锁”时就会发生死锁

出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

多线程编程时应该注意避免死锁的发生

线程间通信的必要性

在生产者和消费者问题中,仅有synchronized是不够的

synchronized可阻止并发更新同一个共享资源,实现同步

synchronized不能用来实现不同线程之间的消息传递(通信)

Java提供了3个方法解决线程之间的通信问题

方法名作用
final void wait()表示线程一致等待,直到其它线程通知
void wait(long timeout)线程等待指定毫秒参数的时间
final void wait(long timeout,int nanos线程等待指定毫秒、微秒的时间
final void notify()唤醒一个处于等待状态的线程
final void notifyAll()唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先运行

均是java.lang.Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异


代码实现如下:

定义产品类

class SharedData{
	private char c;
	private boolean isProduced = false; // 信号量	
	public synchronized void putShareChar(char c) {
		// 如果产品还未消费,则生产者等待
		if (isProduced) {
			try{	System.out.println("消费者还未消费,因此生产者停止生产");
				wait(); // 生产者等待
			} catch (InterruptedException e) {e.printStackTrace(); 	}
		}
		this.c = c;
		isProduced = true; // 标记已经生产
		notify(); // 通知消费者已经生产,可以消费
		System.out.println("生产了产品" + c + "  通知消费者消费...");
	}	
	public synchronized char getShareChar() {
		// 如果产品还未生产,则消费者等待
		if (!isProduced){
			try{  System.out.println("生产者还未生产,因此消费者停止消费");
				wait(); // 消费者等待
			} catch (InterruptedException e) {e.printStackTrace();}
		}
		isProduced = false; // 标记已经消费
		notify(); // 通知需要生产
		System.out.println("消费者消费了产品" + c + "  通知生产者生产...");
		return this.c;
	}
}

定义生产者线程类

//生产者线程
class Producer extends Thread {
	private SharedData s;
	Producer(SharedData s){
		this.s = s;
	}
	public void run(){
		for (char ch = 'A'; ch <= 'D'; ch++){
			try{
				Thread.sleep((int) (Math.random() * 3000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			s.putShareChar(ch); // 将产品放入仓库
		}
	}
}

定义消费者线程类

//消费者线程
class Consumer extends Thread {
	private SharedData s;
	Consumer(SharedData s){
		this.s = s;
	}
	public void run(){
		char ch;
		do {
			try	{
				Thread.sleep((int)(Math.random()*3000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			ch = s.getShareChar(); // 从仓库中取出产品
		} while (ch != 'D');
	}
}

定义测试类

//测试类
class CommunicationDemo{
	public static void main(String[] args){
		//共享同一个共享资源
		SharedData s = new SharedData();
		//消费者线程
		new Consumer(s).start();
		//生产者线程
		new Producer(s).start();
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值