Java学习笔记day18

线程

引入

目前我们的代码在等待用户输入的时候,不能生成随机数

作用

使程序可以同时执行多个事物

名词

进程

简介: 一个正在进行的程序
从内存的角度来看,程序要运行时系统才会为其开辟一片运行内存,程序在运行完毕后系统将回收这片内存

线程

简介: 一个代码的执行路径就是一个线程

多线程

简介: 一个进程中有多个线程
注意: 
	多个线程在宏观上是同时进行的,在微观上多个线程是在不断抢夺CPU执行权,交替执行的

主线程

简介: 一个进程自带一个线程,该线程被称为主线程

子线程

简介: 一个进程除了主线程,都是子线程

同步

多个线程在执行时,必须先执行一个,再执行另外一个,不能同时操作同一个数据

异步

多个线程同时执行,互不干扰

线程与进程的关系

1.一个进程中可以有多个线程
2.一个进程至少要保证有一个前台线程存活,否则该进程也将被销毁
3.进程间数据不能共享,多个线程间数据是可以共享的
4.进程是一片运行内存,线程是一个执行路径

线程的组成(了解)

1.运行数据
	1.栈,每个线程都有其各自的栈内存
	2.堆,多个线程共享一个堆内存
2.CPU时间片
	当线程抢夺到CPU执行权后可执行的时间
3.线程的逻辑

线程的创建(*)

方案1

方式1
思想: 使用一个类继承于Thread,在该类中重写run方法
步骤: 
	1.创建一个类
	2.使该类继承于Thread
	3.在该类中重写run方法
	4.在使用该线程的地方创建该类对象
方式2
思想: 使用匿名内部类的形式创建线程对象

方案2

将线程任务(Runnable)与线程(Thread)分开
方式1
思想: 创建线程任务对象,在创建线程对象时传入线程任务对象
步骤: 
	1.创建一个类,使其实现Runnable接口
	2.重写run方法
	3.创建该类对象,该类对象就是线程任务对象
	4.创建线程对象,传入线程任务对象
方式2
思想: 使用匿名内部类的形式创建线程任务对象

线程的方法

启动(*)

语法: 
	线程对象.start();
注意: 
	使用线程对象调用start()方法才会开启新的执行路径
	如果使用线程对象调用run()方法相当于对象调用方法,并没有开启新的执行路径
//简单售票案例
public class Test05 {
	public static void main(String[] args) {
		new Thread(new MyRunnable0("小红")).start();
		new Thread(new MyRunnable0("小明")).start();
		new Thread(new MyRunnable0("小白")).start();
		new Thread(new MyRunnable0("小黑")).start();
	}
}

class MyRunnable0 implements Runnable{
	private int count = 100;
	private String name;
	
	public MyRunnable0(String name) {
		this.name = name;
	}
	
	@Override
	public void run() {
		while(count > 0) {
			count--;
			System.out.println(name + "销售了1张票, 还剩" + count +"张票");
		}
	}
	
}

运行结果

消亡(*)

语法: 
	线程对象.stop(): 可以让线程停止,但是方法已经过时
	线程对象.destroy(): 是让程序崩溃,该方法也过时了
线程在什么情况下消亡?
	线程执行完run()方法中的代码就会等待系统回收
注意: 
	一般来说开启线程我们将不再管理线程,等线程中run()方法执行完毕后会自动销毁
	线程一经启动不受控制
package demo01;
//手动调用方法销毁线程
public class Test01 {
	public static void main(String[] args) {
		MyThread thread = new MyThread();
		thread.start();
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//销毁线程
		thread.myStop();
	}
}

class MyThread extends Thread{
	private boolean isRun = true;
	
	public void myStop() {
		isRun = false;
	}
	
	@Override
	public void run() {
		for (int i = 0; i < 1000 && isRun; i++) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(i);
		}
	}
}

运行结果

获取当前线程(*)

语法: 
	Thread 当前线程对象 = Thread.currentThread();
	注意: 
		在哪个线程中使用该方法,就是获取哪个线程的对象

名称

设置名称
	方法1: 
		在创建线程对象时传入线程名称
	方法2: 
		在线程启动前通过setName()设置线程名称
			线程对象.setName(线程名称);
	因为线程是有默认名称的,如Thread-0,Thread-1...
	
获取名称
	String 线程名称 = 线程对象.getName();

休眠(*)

作用: 线程等待一会
语法: 
	Thread.sleep(休眠时间);
注意: 
	1.休眠时间单位为毫秒 1000毫秒 == 1秒
	2.在哪个线程中用就是让哪个线程休眠
	3.不会释放锁对象

合并

作用: 将多个线程合并为一个线程
语法: 
	线程对象.join();
注意: 
	对哪个线程使用该方法就是将哪个线程合并到当前线程中

礼让

作用: 放弃本次抢夺到的CPU执行权,重新参与抢夺
语法: 
	Thread.yield();
注意: 
	在哪个线程中使用就是让哪个线程放弃本次抢夺到的CPU执行权

优先级

作用: 增大或减小线程抢夺到CPU执行权的概率
语法: 
	设置优先级
		线程对象.setPriority(优先级);
	获取优先级
		int 线程优先级 = 线程对象.getPriority();
注意: 
	1.优先级取值范围为1~10,超过范围会报错
	2.在线程启动前设置
	3.线程的优先级默认是5

守护线程(*)

子线程分为两种: 
	前台线程
	守护线程(后台线程)
如果创建一个子线程,默认是前台线程
可以通过
	线程对象.setDaemon(true); 将子线程设置为守护线程
	
前台线程与守护线程的区别
	当一个进程有前台线程存活,那么该进程就不会被系统回收,直到当前进程中所有前台线程都执行完毕后,进程才会被系统回收
	如果一个进程所有前台线程执行完毕,还有守护线程正在执行,那么此时不管守护线程是否执行完毕,进程都将被销毁
package demo01;

public class Test02 {
	public static void main(String[] args) {
		Thread thread = new Thread(new Runnable() {			
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(i);
				}
				System.out.println("子线程结束");				
			}
		});
		thread.start();
		System.out.println("主线程结束");
	}
}

运行结果

修改下代码,设置thread线程为守护线程

package demo01;

public class Test02 {
	public static void main(String[] args) {
		Thread thread = new Thread(new Runnable() {			
			@Override
			public void run() {
				for (int i = 0; i < 10; i++) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(i);
				}
				System.out.println("子线程结束");				
			}
		});
		//设置thread线程为守护线程
		thread.setDaemon(true);
		thread.start();
		System.out.println("主线程结束");
	}
}

运行结果

线程的生命周期(*)

初始状态: New
	线程创建后,到启动前
就绪状态: Ready
	线程启动后,但是没有抢夺到CPU执行权,准备抢夺CPU执行权
运行状态: Running
	线程抢夺到CPU执行权,正在执行run方法中的代码
	当CPU执行权时间到了后,
		如果run方法中的代码执行完毕进入终止状态
		如果run方法中的代码没有执行完成,会回到就绪状态
终止状态: Terminated
	当线程执行完run方法中的代码后会进入到该状态,等待被系统回收
等待状态: Timed Waiting
	有限期等待
		如使用线程类调用sleep方法,线程会进入到有限期等待
	无限期等待
		如A线程合并B线程,B线程就会进入无限期等待

练习

1.火车站三个窗口同时销售1000张票
分析: 
	需要3个线程
	线程任务使销售1000张票
		因为是多个窗口同时销售1000张票,所以剩余票的数量应该属于所有售票任务共有的属性
注意: 此时代码存在线程安全问题,以后修改补充...
package demo02;

public class MyRunnable implements Runnable{
	private static int count = 1000;
	
	@Override
	public void run() {
		while(count > 0) {
			//放慢线程速度
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//获取当前线程
			Thread thread = Thread.currentThread();
			//获取线程名称
			String name = thread.getName();
			count--;
			System.out.println(name + "销售了1张票, 还剩" + count +"张票");
		}
	}

}

package demo02;

public class Test {
	public static void main(String[] args) {
		MyRunnable runnable = new MyRunnable();
		new Thread(runnable, "窗口一").start();
		new Thread(runnable, "窗口二").start();
		new Thread(runnable, "窗口三").start();
	}
}

运行结果


此时可以看到,会有线程重复数据,涉及到线程安全问题,明天补充~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值