day11-多线程

多线程
一、相关概念
1.程序、进程、线程三者的区别?
程序:我们每天写的代码(代码是死的)
//比如:没有运行的飞秋,它就是一个程序
进程:运行起来的代码
//比如:运行起来的飞秋,它就是一个进程

		线程:是"进程"中的单个顺序控制流,是一条执行路径
			//我们使用qq可以一边聊天、一边下载     聊天时一个线程、下载也是一个线程

	2.进程和线程之间的关系
		(1)线程是依赖于进程的   //我想和乔梦佳同学聊天,我就必须先起启动qq这个进程
		(2)一个进程中可以有多个线程	//电脑管家这个进程,里面包含病毒查杀、垃圾清理等多个线程
				进程是线程的一个集合(容器),真正干活的是线程

	3.小问题
		(1)电脑管家中病毒查杀、垃圾清理、电脑加速多个线程,请问他们是同时执行的吗?
				不是,在同一个时间点CPU只能做一件事。之所以你看起来像是在同时做多件事,是因为CPU在做着高速的切换

		(2)电脑管家中病毒查杀、垃圾清理,他们之间的执行有没有顺序?
				没有顺序,抢占式调度模型(谁抢到谁执行)

	4.多线程的好处
		我们可以一边玩游戏,一边听音乐。无形中提搞了CPU的使用效率。提高了CPU的执行效率



二、实现多线程的第一种方式-----继承Thread类   //☆☆☆☆☆

	1.步骤
		(1)定义一个类MyThread继承Thread类
		(2)在MyThread类中重写run()方法
		(3)创建MyThread类的对象
		(4)启动线程

		代码实现:
				public class MyThread extends Thread {
					@Override
					public void run() {
						for (int i = 0; i < 100; i++) {
							System.out.println(i);
						}
					}
				}
			------------------------------------------------------
			public class Demo1 {
					public static void main(String[] args) {
						MyThread m1 = new MyThread();
						MyThread m2 = new MyThread();

						m1.start();
						m2.start();
					}
				}

	2.在多线程中run方法和start方法的区别?  //面试题
		
		void run() :里面的内容是线程需要执行的逻辑,直接调用相当于普通方法
		void start(): 使此线程开始执行,Java虚拟机(进程)会调用run方法()

		//Java的运行原理
				编写代码----编译(javac)----运行(java)
			当我们执行java命令的时候,启动一个JVM(Java虚拟机),这里的JVM其实就是一个进程,然后去创建一个名为main的线程,调用该线程里面的main方法
			
	3.设置和获取线程的名称
		(1)如果我不给线程设置名称,有没有名称
				有 
				Thread-x   x从0开始的

		(2)线程设置名称的方式
			*使用setName(String name)方法
				//一定要在创建线程对象之后再使用该方法
				MyThread m1 = new MyThread();
				m1.setName("飞机");
				MyThread m2 = new MyThread();
				m2.setName("火车");

			*使用构造方法的方式
				//需要在线程类MyThread中添加一个带String类型的构造方法
				MyThread m1 = new MyThread("飞机");
				MyThread m2 = new MyThread("火车");
		
		(3)获取线程名称的方式
			*使用getName()方法获取。
				 public void run() {
					for (int i = 0; i < 100; i++) {
						System.out.println(this.getName()+":"+i);  //在线程类中的run方法里来获取线程名称
					}
				}

			*使用Thread.currentThread().getName()方法
				public void run() {
					for (int i = 0; i < 100; i++) {
						System.out.println(Thread.currentThread().getName()+":"+i);
					}
				}

	4.线程的优先级   //了解
		final int getPriority() 返回此线程的优先级
		final void setPriority(int newPriority)更改此线程的优先级 线程默认优先级是5;线程优先级的范围是:1-10			
		
		(1)线程优先级的范围
				1-10
	
		(2)线程的默认优先级
				5
				
		(3)线程的优先级越高,代表获取到CPU的执行权的概率越高,并不能保证一定能抢到
	
	5.线程的控制  //了解
		(1)static void sleep(long millis) 使当前正在执行的线程停留(暂停执行)指定的毫秒数
			*使用位置:一般用在run方法中,参数为毫秒值
			*使线程进入休眠状态,这个时候就不会去争夺CPU的执行权
	
		(2)void join() 等待这个线程死亡
			*使用位置:一般用在main方法中,一定要在start方法之后使用
			*设置为加入线程后,会优先执行
	
		(3)void setDaemon(boolean on)将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
			*使用位置:一般用在main方法中,一定要在start方法之前使用
			*当被守护线程执行结束之后,守护线程将结束,但是在临死前会挣扎着执行一点代码
	
	6.线程的生命周期  //常见的面试题  ☆☆☆☆☆
		//至少要知道有几种状态    最好能 "独立" 画图
			新建、就绪、运行、死亡、阻塞
			
	
三、多线程的第二种实现方式  ------  实现Runnable接口的方式	//☆☆☆☆☆
	1.实现步骤
		(1)定义一个类MyRunnable实现Runnable接口
		(2)在MyRunnable类中重写run()方法
		(3)创建MyRunnable类的对象
		(4)创建Thread类的对象,把MyRunnable对象作为构造方法的参数
		(5)启动线程
			代码实现:
				public class MyRunnable implements Runnable {
						@Override
						public void run() {
							for (int i = 0; i < 100; i++) {
								System.out.println(i);
							}
						}
					}
			-----------------------------------------------------------
			public class Demo2 {
				public static void main(String[] args) {
					MyRunnable mr1 = new MyRunnable();
					MyRunnable mr2 = new MyRunnable();

					Thread t1 = new Thread(mr1);
					Thread t2 = new Thread(mr2);

					t1.start();
					t2.start();
				}
			}
	
	2.怎么给第二种方式的线程设置名称
		*使用setName(String name)来设置名称
			Thread t1 = new Thread(mr1);
			t1.setName("灰机");
			Thread t2 = new Thread(mr2);
			t2.setName("火车");
			
		*使用构造方法设置线程名称
			Thread t1 = new Thread(mr1,"灰机");
			Thread t2 = new Thread(mr2,"火车");
	
	3.获取线程名称 
		//只能使用一种方式获取
		*使用Thread.currentThread().getName()来获取
			 public void run() {
					for (int i = 0; i < 100; i++) {
						System.out.println(Thread.currentThread().getName()+":"+i);  //只能使用这种方式来获取线程名称,不能使用this.getName()
					}
				}
	
	4.两种多线程实现方式的对比
		(1)继承Thread类
			*只能单继承
			*增加了类和类之间的耦合性
			
		
		(2)实现Runnable接口
			*支持多实现
			*将"线程"(Thread)和"线程任务"(run)之间解了耦合  //run方法不在线程类中了

//------------------------------------------------------------------------
重点一:properties类
重点二:多线程的两种实现方式

//-----------------------------------------------------------------------

四、买票案例
	//问题:多个线程之间是怎么实现共享100张票的?
			*实现Runnable接口方式:
			将100张票封装在SellTicket类中,创建了一个SellTicket对象,然后将这个对象共享给三个线程,这样的话三个线程就共享了这个对象,也就共享了里面的100张票
	
		*继承Thread类的方式
			//使用static修饰ticket,这个时候三个线程类对象就共享了这个静态变量
			public class SellTicket extends Thread{

				private static int ticket = 100;
				@Override
				public void run() {
					while (true){
						if (ticket<=0){
							break;
						}else{
							System.out.println(this.getName()+"正在卖第"+ticket+"张票");
							ticket--;
						}
					}
				}
			}
	----------------------------------------------------------------------------------------------------------------
	public class Demo1 {
			public static void main(String[] args) {
				SellTicket s1 = new SellTicket();
				s1.setName("窗口一");
				SellTicket s2 = new SellTicket();
				s2.setName("窗口二");
				SellTicket s3 = new SellTicket();
				s3.setName("窗口三");

				s1.start();
				s2.start();
				s3.start();
			}
		}
	
	1.卖票案例的两个小问题
		(1)卖了重复票
		(2)卖了负数票
		
		//出现这两个问题的根本原因:CPU的执行权被哪个线程抢到是随机的,具有不确定性
	
	2.安全问题出现的条件
		(1)是多线程环境
		(2)有共享数据
		(3)有多条语句"操作"共享数据   //操作:增、删、改
	
	3.使用同步代码块解决线程安全问题//☆☆☆☆☆
		(1)格式
			synchronized(任意对象) {
				多条语句操作"共享数据"的代码
			}
			
		(2)任意对象指的是什么?
			其实就是我们所说的锁对象,对于给出什么样的锁对象都无所谓,//但是一定要保证多个线程使用的是同一把锁
			
		(3)大括号到底要包含哪些内容?
				//根据共享数据去找
			*怎么使用
				使用idea自动生成:选中要包含的代码--------使用快捷键CTRL + ALT + T  -----------选择synchronized
					
	4.同步方法
		(1)同步非静态方法
			*格式
				修饰符  synchronized  返回值  方法名(参数列表){
					xxxxx
				}
				
				举例:
					public synchronized void sell(){
						xxxxx
					}
					
				//同步非静态方法的锁对象是谁?
					//this
		
		
		(2)同步静态方法
			*格式
				修饰符 static synchronized 返回值  方法名(参数列表){
					xxxxxxxxx
				}
	
			//同步静态方法的锁对象是谁?
				类名.class
	
	
五、线程安全的类
	StringBuilder(线程不安全的、效率高)  ----------  StringBuffer(线程安全的、效率低)
	ArrayList(线程不安全的、效率高)  ------------------  Vector(线程安全的、效率低)
	HashMap(线程不安全的、效率高)  ------------------Hashtable(线程安全的、效率低)
		//面试题:HashMap和HashTable的区别?
			HashMap是线程不安全的、效率高    允许null键和null值
			Hashtable是线程安全的、效率低    不允许null键和null值
	
	Collections.synchronizedList(new ArrayList<String>());
		//线程安全的类之所以安全,是因为在它的底层方法或者代码上加上了synchronized
		
		
		
六、Lock锁   //(不常用)		
	1.为什么会出现Lock锁
		因为Lock锁更加具有面向对象的意思,是用来替代同步代码块的,同步代码块更加具有一点C语言的面向过程的味道
		
		
	2.相关的类和方法
		*构造方法:ReentrantLock() 创建一个ReentrantLock的实例
		
		*相关的方法
			void lock() 获得锁
			void unlock() 释放锁
		
	3.怎么使用?
		synchronized(任意对象) {                            //---------------------lock.lock();
				多条语句操作"共享数据"的代码
			}												//---------------------lock.unLock();
		
		代码举例:
				public void run() {

				while (true){
		//            synchronized (obj) {
					lock.lock();
						if (ticket<=0){
							break;
						}else{
							//1  //2 //3
							System.out.println(Thread.currentThread().getName()+"正在卖第"+ticket+"张票");
							ticket--;
						}
					// }
					lock.unlock();
				
				}
			}
		
	4.异常处理
		try---finally
			因为如果在lock()和unLock()方法之间出现了异常,那么就会导致关闭锁资源的代码无法被执行,为了让锁对象一定会被释放,我们就是用finally
		
		//回忆
			(1)try  ---  catch
			(2)try  ---catch -- finally
			(3)try--finally
			(4)catch ---finally
			(5)try---catch---catch
		
七、生产者和消费者
	//Object类的等待和唤醒方法
		void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
			特点:死等   不会自动醒来,除非被唤醒
		
		void notify() 唤醒正在等待对象监视器的"单个"线程  //只能唤醒单个线程
		void notifyAll() 唤醒正在等待对象监视器的"所有"线程  //可以唤醒所有线程
		
		//注意:这三个方法必须由锁对象来调用     ☆☆☆☆☆
		
	1.在生产者和消费者模式中,一共出现了几个类?
		(1)奶箱类
			*奶箱状态
			*数量
			
		(2)生产者类
			判断奶箱的状态
				*有牛奶
					不生产---进入等待状态
				*没有牛奶
					生产牛奶
						唤醒消费者线程去消费
						改变奶箱的状态为有牛奶
		
		(3)消费者类
			判断奶箱的状态 
				*有牛奶
					消费牛奶
					唤醒生产者去生产
					奶箱的状态改成没有牛奶
					
				*没有牛奶
					不消费----进入等待状态
		
		(4)测试类
		
	2.套路  ----针对于生产者和消费者
		(1)while(true)
		(2)synchronized(){}
		(3)判断数量
		(4)判断状态
		
	3.作业
		吃货吃包子
			(1)吃货类
			(2)老板类
			(3)袋子类  //50个包子
			(4)测试类
		代码实现:
			public class Bag {
				public int num = 50;
				public boolean status = false;  // true代表袋子里有包子  false表示袋子里没有包子
			}
		
		----------------------------------------------------------------------------------------------
			public class Boss implements Runnable {
					private Bag bag;

					public Boss(Bag bag) {
						this.bag = bag;
					}

					@Override
					public void run() {
						while (true){
							synchronized (Bag.class){
								if (bag.num==0){
									break;
								}

								if (bag.status){
									try {
										Bag.class.wait();
									} catch (InterruptedException e) {
										e.printStackTrace();
									}
								}else{
									System.out.println("老板正在做第"+bag.num+"个包子");
									bag.status=true;
									Bag.class.notifyAll();

								}
							}

						}

					}
				}
		--------------------------------------------------------------------------------------
		public class ChiHuo implements Runnable {
				private Bag bag;

				public ChiHuo(Bag bag) {
					this.bag = bag;
				}

				@Override
				public void run() {
					while (true){
						synchronized (Bag.class){
							if (bag.num==0){
								break;
							}

							if (!bag.status){
								try {
									Bag.class.wait();
								} catch (InterruptedException e) {
									e.printStackTrace();
								}
							}else{
								System.out.println("吃货正在吃第"+bag.num+"个包子");
								bag.num--;
								bag.status=false;
								Bag.class.notifyAll();

							}
						}

					}
				}
			}
			
		--------------------------------------------------------------------------------------
		public class Demo1 {
			public static void main(String[] args) {
				Bag bag = new Bag();

				Boss b1= new Boss(bag);
				ChiHuo c1 = new ChiHuo(bag);

				Thread t1 = new Thread(b1);
				Thread t2 = new Thread(c1);

				t1.start();
				t2.start();
			}
		}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值