【Java并发编程】-【多线程基础】

一、多线程基础

1.多线程概述

进程:
正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。
线程:
是进程中的单个顺序控制流,是一条执行路径,一个进程如果只有一条执行路径,则称为单线程程序。一个进程如果有多条执行路径,则称为多线程程序。

2.Java程序运行原理

java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。

3.多线程的实现

-多线程的实现方案1:继承Thread类

该类要重写run()方法
不是类中的所有代码都需要被线程执行的。为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。

 public class MyThread extends Thread {

	@Override
	public void run() {
		for (int x = 0; x < 200; x++) {
			System.out.println(x);
		}
	}

}

思考1

run()和start()的区别?

答:

run():仅仅是封装被线程执行的代码,直接调用是普通方法。
start():首先启动了线程,然后再由jvm去调用该线程的run()方法。

获取和设置线程名称

public final String getName()
获取线程名称。
public final void setName(String name)
设置线程名称。
public static Thread currentThread()
获取main方法所在的线程名称,可以获取任意方法所在的线程名称。

-多线程的实现方案2:实现Runnable接口

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int x = 0; x < 100; x++) {
			// 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
			System.out.println(Thread.currentThread().getName() + ":" + x);
		}
	}

}

4.线程调度

线程有两种调度模型:
分时调度模型
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片。
抢占式调度模型
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。
Java使用的是抢占式调度模型。
设置和获取线程优先级
public final int getPriority()
获取线程优先级。
public final void setPriority(int newPriority)
设置线程优先级。

5.线程控制

①public static void sleep(long millis)
线程休眠。
②public final void join()
线程加入,等待该线程终止。
③public static void yield()
线程礼让,暂停当前正在执行的线程对象,并执行其他线程。
④public final void setDaemon(boolean on)
后台线程,将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。
⑤public final void stop()
让线程停止,过时了,但是还可以使用。
⑥public void interrupt()
抛出一个InterruptedException,并执行其他线程。
线程生命周期图
在这里插入图片描述

二、线程安全问题

1.线程安全问题因素

①是否是多线程环境
②是否有共享数据
③是否有多条语句操作共享数据

2.解决线程安全问题实现1:同步代码块

格式:

	synchronized(对象){
		需要同步的代码;
	}

同步代码块的锁对象是任意对象。
同步的代码是多条语句操作共享数据的代码的部分。

3.同步的特点

- 同步的前提
①多个线程
②多个线程使用的是同一个锁对象
- 同步的好处
同步的出现解决了多线程的安全问题。
- 同步的弊端
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

4.解决线程安全问题实现2:同步方法

同步方法就是把同步关键字加到方法上。
格式:

private static synchronized void sellTicket() {
		if (tickets > 0) {
		try {
				Thread.sleep(100);
		} catch (InterruptedException e) {
				e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()
					+ "正在出售第" + (tickets--) + "张票 ");
		}

静态方法的锁对象是类的字节码文件对象。
如果锁对象是this,就可以考虑使用同步方法。否则能使用同步代码块的尽量使用同步代码块。

5.JDK5中Lock锁的使用

为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。
void lock()
获取锁。
void unlock()
释放锁。
ReentrantLock是Lock的实现类.。

// 定义锁对象
private Lock lock = new ReentrantLock();

try {
	// 加锁
	lock.lock();
	//此处为同步代码
} finally {
	// 释放锁
	lock.unlock();
}

6.死锁问题

同步弊端
效率低,如果出现了同步嵌套,就容易产生死锁问题。
死锁问题
是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象。

public void run() {
	if (flag) {
		synchronized (MyLock.objA) {
			System.out.println("if objA");
			synchronized (MyLock.objB) {
				System.out.println("if objB");
			}
		}
	} else {
		synchronized (MyLock.objB) {
			System.out.println("else objB");
			synchronized (MyLock.objA) {
				System.out.println("else objA");
			}
		}
	}
}

三、线程间通信问题

不同种类的线程间针对同一个资源的操作。在这里插入图片描述
Java等待唤醒机制
Object类中提供了三个方法:
wait():等待
notify():唤醒单个线程
notifyAll():唤醒所有线程
这些方法的调用必须通过锁对象调用。

public class SetThread implements Runnable {

	private Student s;
	private int x = 0;

	public SetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {
			synchronized (s) {
				//判断有没有
				if(s.flag){
					try {
						s.wait(); //t1等着,释放锁
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				if (x % 2 == 0) {
					s.name = "马云";
					s.age = 50;
				} else {
					s.name = "刘织忋";
					s.age = 24;
				}
				x++; //x=1
				
				//修改标记
				s.flag = true;
				//唤醒线程
				s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。
			}
			//t1有,或者t2有
		}
	}
}

public class GetThread implements Runnable {
	private Student s;

	public GetThread(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while (true) {
			synchronized (s) {
				if(!s.flag){
					try {
						s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
				System.out.println(s.name + "---" + s.age);
				
				//修改标记
				s.flag = false;
				//唤醒线程
				s.notify(); //唤醒t1
			}
		}
	}
}

线程的状态转换图及常见执行情况
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值