线程

Day19

概念

一、什么是进程

进程是系统进行资源分配和调用的独立单元,每一个进程都有它的独立内存空间和系统资源。

二、单进程操作系统和多进程操作系统的区别

单进程操作系统:dos(一瞬间只能执行一个任务)

多进程单用户操作系统:Windows(一瞬间只能执行多个任务)

多进程多用户操作系统:Linux(一瞬间只能执行多个任务)

三、现在的多核CPU是否可以让系统在同一个时刻可以执行多个任务吗?

理论上是可以的

四、什么是线程,理解线程和进程的关系

什么是线程?

线程是进程里面的一条执行路径,每个线程同享进程里面的内存空间和系统资源

一个进程 可以有 多个线程:各个线程都有不同的分工

理解线程和进程的关系

进程 与 进程 之间的关系:进程之间的内存空间和系统资源是独立的

同一个进程里的多条线程 :线程之间的内存空间和系统资源是共享的

进程里:可以有一条或一条以上的线程

进程里只有一条线程的情况下,这条线程就叫做主线程

进程里有多条线程的情况下,只有一条线程叫做主线程

Ps:线程是在进程里的,他们是包含关系

五、我们应用的软件有哪些是多线程的应用?

都是

程序编写

一、创建线程

创建线程 – 线程类的方式

步骤:
1.创建MyThread类,继承Thread,重写父类的run方法
2.创建子线程对象
3.启动子线程

//线程类
public class MyThread extends Thread{

	//当前线程对象抢到cpu资源后调用的方法
	@Override
	public void run() {
		System.out.println("子线程被调用了");
	}
}
public static void main(String[] args) {
		
		//创建子线程对象
		MyThread t = new MyThread();
		
		//启动子线程
		t.start();
	}

创建线程 – 任务类的方式

步骤:
1.创建Task,实现Runnable接口里的run方法
2.创建任务类对象
3.创建子线程对象,并把任务提交给子线程
4.启动子线程

//任务类
public class Task implements Runnable{

	//当前线程对象抢到CPU资源后调用的方法
	@Override
	public void run() {
		System.out.println("任务被调用了");
	}

}

public static void main(String[] args) {
		
		//创建任务类对象
		Task task = new Task();
		
		//创建子线程对象,并把任务提交给子线程
		Thread t = new Thread(task);
		
		//启动子线程
		t.start();
	}

二、多线程争抢资源场景

知识点:感受多线程之间争抢资源的场景

需求:编写一个多线程的应用程序,
主线程打印1-100之间的数字,
子线程打印200-300之间的数字,
观察其输出的结果,体会多线程互相争抢资源的场景

public class MyThread extends Thread{

	@Override
	public void run() {
		
		for (int i = 200; i <= 300; i++) {
			System.out.println("子线程:" + i);
		}
		
	}
}

public static void main(String[] args) {
		
		MyThread t = new MyThread();
		t.start();
		
		for (int i = 1; i <= 100; i++) {
			System.out.println("主线程:" + i);
		}
		
	}

注意:主线程在调用main方法时就会开启,方法结束才关闭,因此开启子线程需要在for循环上面,否则就会主线程执行完for循环才开启子线程。

经典面试题:请问当我们编写一个单纯的main方法时,此时该程序是否为单线程的?为什么?
多线程,垃圾回收器是一个后台线程。

三、线程的优先级别

需求:在主线程中创3个子线程,并且设置不同优先级,观察其优先级对线程执行结果的”影响”。

优先级别:1~10(数字越大优先级越高)

注:线程的优先级只能影响抢到CPU资源的概率,不是决定性的!

public class A extends Thread{

	@Override
	public void run() {
		for (int i = 1; i <= 100; i++) {
			System.out.println("A:" + i);
		}
	}
}

public class B extends Thread{

	@Override
	public void run() {
		for (int i = 1; i <= 100; i++) {
			System.out.println("B:" + i);
		}
	}
}

public class C extends Thread{

	@Override
	public void run() {
		for (int i = 1; i <= 100; i++) {
			System.out.println("C:" + i);
		}
	}
}
public static void main(String[] args) {
		
		A a = new A();
		B b = new B();
		C c = new C();
		
		a.setPriority(Thread.MAX_PRIORITY);//10
		b.setPriority(Thread.NORM_PRIORITY);//5
		c.setPriority(Thread.MIN_PRIORITY);//1
		
		a.start();
		b.start();
		c.start();
		
	}

关于该需求的代码可以进行优化,为避免相同功能线程类重复创建,可以给线程命名:

四、线程命名

//命名方法一:在子线程类中设置属性
public class MyThread extends Thread{

	private String threadName;
	
	public MyThread(String threadName) {
		this.threadName = threadName;
	}

	@Override
	public void run() {
		for (int i = 1; i <= 100; i++) {
			System.out.println(threadName + ":" + i);
		}
	}
}
//命名方法二:调用父类Thread的有参构造设置属性,并用父类的getName方法
public class MyThread extends Thread{

public MyThread(String name) {
   super(name);
}

@Override
public void run() {
   for (int i = 1; i <= 100; i++) {
//			System.out.println(super.getName() + ":" + i);
   
   	//Thread.currentThread()获取当前线程对象
   	System.out.println(Thread.currentThread().getName() + ":" + i);
   }
}
}
public static void main(String[] args) {
		
		MyThread a = new MyThread("A");
		MyThread b = new MyThread("B");
		MyThread c = new MyThread("C");
		
		a.setPriority(Thread.MAX_PRIORITY);//10
		b.setPriority(Thread.NORM_PRIORITY);//5
		c.setPriority(Thread.MIN_PRIORITY);//1
		
		a.start();
		b.start();
		c.start();
		
	}

五、线程的生命周期

在这里插入图片描述

六、线程的休眠

需求:编写一个抽取学员回答问题的程序,要求倒数三秒后输出被抽中的学员姓名

Thread.sleep(1000); -> 让当前线程进入到阻塞状态,1000毫秒后进入到就绪状态

此方法为静态方法,写在哪个线程中,哪个线程就休眠

public static void main(String[] args) throws InterruptedException {
		
		String[] names = {"小宇","小亮","小丹","小蒲","雪飞","小康","小升","小怡"};
		
		Random ran = new Random();
		int index = ran.nextInt(names.length);
		
		for (int i = 3; i >= 1;i--) {
			System.out.println(i);
			
			Thread.sleep(1000);
		}
		
		System.out.println(names[index]);
	}

七、线程的礼让

需求:创建两个线程A,B,分别各打印1-100的数字,其中B一个线程,每打印一次,就礼让一次,观察实验结果

礼让:让当前线程退出CPU资源,该线程马上进入到就绪状态

Thread.yield() 写在哪个线程里,哪个线程就礼让

public static void main(String[] args) {
		
		A a = new A();
		B b = new B();
		
		a.start();
		b.start();
	}
public class A extends Thread{
	
	@Override
	public void run() {
		for (int i = 1; i <= 100; i++) {
			System.out.println("A:" + i);
		}
	}
}
public class B extends Thread{
	
	@Override
	public void run() {
		for (int i = 1; i <= 100; i++) {
			System.out.println("B:" + i);
			
			//礼让:让当前线程退出CPU资源,该线程马上进入到就绪状态
			Thread.yield();
		}
	}
}

八、知识点:线程的合并

需求:主线程和子线程各打印200次,从1开始每次增加1,当主线程打印到10之后,让子线程先打印完再打印主线程。

public static void main(String[] args) throws InterruptedException {
		
		MyThread t = new MyThread();
		t.start();
		
		for (int i = 1; i <= 200; i++) {
			System.out.println("主线程:" + i);
			if(i == 10){
				//合并:让t线程加入到当前线程里
				t.join();
			}
		}
	}
public class MyThread extends Thread{

	@Override
	public void run() {
		for (int i = 1; i <= 200; i++) {
			System.out.println("子线程:" + i);
		}
	}
}

九、知识点:线程的销毁

用stop()方法(不推荐,不知道执行到线程哪个地方)

public static void main(String[] args) throws InterruptedException {
		
		MyThread t = new MyThread();
		t.start();
		// 主线程休眠 3 秒,以便让 MyThread 运行一段时间
		Thread.sleep(3000);
		
		//关闭线程 - 立即关闭
		t.stop();
		
	}
public class MyThread extends Thread{

	@Override
	public void run() {
		while(true){
			System.out.println("111");
			System.out.println("222");
			System.out.println("333");
			System.out.println("444");
		}
	}
}

设置flag

public static void main(String[] args) throws InterruptedException {
		
		MyThread t = new MyThread();
		t.start();
		
		Thread.sleep(3000);
		
		//关闭线程
		t.setFlag(false);
		
	}
public class MyThread extends Thread{
	
	private boolean flag = true;

	@Override
	public void run() {
		while(flag){
			System.out.println("111");
			System.out.println("222");
			System.out.println("333");
			System.out.println("444");
		}
	}


	public void setFlag(boolean flag) {
		this.flag = flag;
	}
}

t.interrupt() - 改变线程存活状态

Thread.currentThread().isInterrupted() - 判断线程存货状态(false-未销毁 true-销毁)

public class Test01 {
	/**
	 * 知识点:线程的销毁
	 */
	public static void main(String[] args) throws InterruptedException {
		
		MyThread t = new MyThread();
		t.start();
		
		Thread.sleep(3000);
		
		//关闭线程
		t.interrupt();//改变线程存活状态
		
	}
	
}

public void run() {
		
		//Thread.currentThread().isInterrupted() - 判断线程存货状态(false-未销毁 true-销毁)
		
		while(!Thread.currentThread().isInterrupted()){
			System.out.println("111");
			System.out.println("222");
			System.out.println("333");
			System.out.println("444");
		}
	}

十、守护线程

知识点:守护线程

理解:默默守护着前台线程,当所有的前台线程都消亡后,守护线程会自动消亡

注意:垃圾回收器就是守护线程

public class MyThread extends Thread{
	
	@Override
	public void run() {
		while(true){
			System.out.println("后台线程默默守护着前台线程");
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

注意:为什么这里不能throws?因为父类没有抛异常,子类就不能抛。

public static void main(String[] args) throws InterruptedException {
		
		MyThread t = new MyThread();
		t.setDaemon(true);//将当前线程设置为守护线程
		t.start();
		
		for (int i = 1; i <= 5; i++) {
			System.out.println("主线程:" + i);
			Thread.sleep(1000);
		}
	
	}

十一、线程局部变量(实现线程范围内的共享变量)

需求:共享单个数据

public class A {

	public void println(){
		
		Thread thread = Thread.currentThread();
		Integer value = Test01.map.get(thread);
		System.out.println(thread.getName() + "里的A类对象获取了数据:" + value);
		
	}
}
public class B {

	public void println(){
		
		Thread thread = Thread.currentThread();
		Integer value = Test01.map.get(thread);
		System.out.println(thread.getName() + "里的B类对象获取了数据:" + value);
	}
}



import java.util.concurrent.ConcurrentHashMap;

public class Test01 {
	/**
	 * 知识点:线程局部变量共享
	 * 
	 * 需求:共享单个数据
	 */
	
	public static ConcurrentHashMap<Thread, Integer> map  = new ConcurrentHashMap<>();
	
	public static void main(String[] args) {
		
		new Thread(new  Runnable() {
			
			@Override
			public void run() {
				int i = 10;
				
				map.put(Thread.currentThread(), i);
				
				A a = new A();
				B b = new B();
				a.println();//10
				b.println();//10
			}
		}, "线程1").start();
		
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				int i = 20;
				
				map.put(Thread.currentThread(), i);
				
				A a = new A();
				B b = new B();
				a.println();//20
				b.println();//20
			}
		}, "线程2").start();
	}
}

共享多个数据

//数据容器类
public class Data {

	private int i;
	private String str;
	
	public Data(int i, String str) {
		this.i = i;
		this.str = str;
	}

	public int getI() {
		return i;
	}

	public void setI(int i) {
		this.i = i;
	}

	public String getStr() {
		return str;
	}

	public void setStr(String str) {
		this.str = str;
	}

	@Override
	public String toString() {
		return i + " -- " + str;
	}
}


public class A {

	public void println(){
		
		Thread thread = Thread.currentThread();
		Data value = Test01.map.get(thread);
		System.out.println(thread.getName() + "里的A类对象获取了数据:" + value);
		
	}
}


public class B {

	public void println(){
		
		Thread thread = Thread.currentThread();
		Data value = Test01.map.get(thread);
		System.out.println(thread.getName() + "里的B类对象获取了数据:" + value);
	}
}
public static ConcurrentHashMap<Thread, Data> map  = new ConcurrentHashMap<>();
	
	public static void main(String[] args) {
		
		new Thread(new  Runnable() {
			
			@Override
			public void run() {
				int i = 10;
				String str = "用良心";
				Data data = new Data(i, str);
				
				map.put(Thread.currentThread(), data);
				
				A a = new A();
				B b = new B();
				a.println();//10
				b.println();//10
			}
		}, "线程1").start();
		
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				int i = 20;
				String str = "做自己";
				Data data = new Data(i, str);
				
				map.put(Thread.currentThread(), data);
				
				A a = new A();
				B b = new B();
				a.println();//20
				b.println();//20
			}
		}, "线程2").start();
	}

需求:共享多个数据 - 使用ThreadLocal

//数据容器类
public class Data {

	private int i;
	private String str;
	
	private Data(int i, String str) {
		this.i = i;
		this.str = str;
	}
	
	//该方法保证了当前线程只能有一个Data对象
	public static Data getIntance(int i ,String str){
		//获取当前线程的Data对象
		Data data = Test01.local.get();
		//判断data对象是否存在
		if(data == null){
			data = new Data(i, str);
		}else{
			data.setI(i);
			data.setStr(str);
		}
		return data;
	}

	public int getI() {
		return i;
	}

	public void setI(int i) {
		this.i = i;
	}

	public String getStr() {
		return str;
	}

	public void setStr(String str) {
		this.str = str;
	}

	@Override
	public String toString() {
		return i + " -- " + str;
	}
}



public class A {

	public void println(){
		
		Thread thread = Thread.currentThread();
		Data value = Test01.local.get();
		System.out.println(thread.getName() + "里的A类对象获取了数据:" + value);
		
	}
}


public class B {

	public void println(){
		
		Thread thread = Thread.currentThread();
		Data value = Test01.local.get();
		System.out.println(thread.getName() + "里的B类对象获取了数据:" + value);
	}
}
public static ThreadLocal<Data> local = new ThreadLocal<>();
	
	public static void main(String[] args) {
		
		new Thread(new  Runnable() {
			
			@Override
			public void run() {
				int i = 10;
				String str = "用良心做教育";
				Data data = Data.getIntance(i, str);
				
				local.set(data);
				
				A a = new A();
				B b = new B();
				a.println();//10
				b.println();//10
			}
		}, "线程1").start();
		
		
		new Thread(new Runnable() {
			@Override
			public void run() {
				int i = 20;
				String str = "做真实的自己";
				Data data = Data.getIntance(i, str);
				
				local.set(data);
				
				A a = new A();
				B b = new B();
				a.println();//20
				b.println();//20
			}
		}, "线程2").start();
	}

练习

1.计算任务,一个包含了2万个整数的数组,分拆了多个线程来进行并行计算,最后汇总出计算的结果。

2.铁道部发布了一个售票任务,要求销售1000张票,要求有3个窗口来进行销售,请编写多线程程序来模拟这个效果(该题涉及到线程安全,https://www.jb51.net/article/221008.htm)

i. 窗口001正在销售第1张票

ii. 窗口001正在销售第2张票

iii. 窗口002正在销售第3张票

iv. 。。。

v. 窗口002正在销售第1000张票

涉及到线程安全,要加锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值