【黑马程序员】多线程

 

 

 

 

          

   一.多线程概述     

一、进程和线程

 进程 正在进行的程序,一个进程可以有多条执行路径,这每一个路径,就称为线程,一个进程至少有一个线程

线程,程序中的控制单元或者执行路径,线程控制着进程的执行

创建线程的目的?为了让线程去执行一些代码。

1. 主线程----vm执行的线程 main线程由jvm开启。一句话总结:主线程负责开启其他线程!

 

2. 垃圾回收线程---更细节说明jvm,jvm启动的 不止一个主线程,还有垃圾回收线程【一般做题慎重考虑】

 

二、多线程存在的意义,

【多个程序同时执行】---Cpu在某一时刻只能执行一个程序,程序的同时执行 是cpu在快速切换,

 

三、线程的创建方式以及注意点总结

1. 有两种方法

方式一:继承Thread类,复写Thread类中的run方法--创建子类对象【就是创建好了一个线程】--对象.start()启动线程,

方法二:继承Runnable接口,实现run方法,通过Thread创建线程,接收Runnble子类对象,,再通过线程对象start方法启动线程

 

2. 为什么要覆盖run方法?

Thread类和Runnable接口中的run方法,用于存储线程运行的代码。子类复写run()

方法就是为了封装自定义线程要执行的代码

【科普:Thread类具有调用底层的方法,控制windows系统创建线程】

3. start()和run()的区别?

【start方法:开启线程并执行该线程的run方法】

【对象.run只是对象调用该方法,线程创建了,并没有执行,run方法只是封装了自定义线程要执行的代码】

 4.Runnable接口和Thread类的区别

① Thread类【线程类】也是继承了Runnable的run方法, run方法的作用,只是存放需要通过线程执行的语句,即只是一个存放功能

② 如果要创建线程,必须通过Thread类或者Thread子类对象启动

 

四、多线程的特性---随机

a. 现象:程序运行结果每一次都不同

是因为多个线程都获取cpu的执行权,cpuu执行到谁,就运行谁,【单核中,某一时刻,只有一个程序在运行】

b. 随机性:谁抢到cpu的执行权,就执行谁,至于执行多长时间,是cpu说了算,

 

五.获取线程对象和名称

currentThread() 返回对当前正在执行的线程对象的引用。

setName() 设置线程名称。

getName():获取线程名称   Thread.currentThread.getName();

 

.线程的状态

 

 

 

二.多线程示例

 

多线程卖票:

 

 

class Demo2 implements Runnable
{
	private int ticket =30;
	public void run()
	{
		for(;ticket>0;)
		{
			try
			{
				Thread.sleep(100);//定义在for循环之内!
			} 
			catch (InterruptedException e) 
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread()+"  run "+ticket--);
		}
	}
}
public class Ticket2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Demo2 d=new Demo2();
		
//4个线程 只关联接收一个 run方法和一个成员 ticket,表示共享同一个对象的成员数据
		Thread t1=new Thread(d);
		Thread t2=new Thread(d);
		Thread t3=new Thread(d);
		Thread t4=new Thread(d);
//启动线程	
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}


 出现了多线程的安全问题

同票:没关联对象,没有共享ticket的值

错票:  0和负数原因

当多条语句在操作同一个线程共享数据ticket时,一个线程对共享的【多条语句】只执行了一部分,就被另一个线程抢走了运行权,导致共享数据错误。

 如何解决?

对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

Java对于多线程的安全问题提供了专业的解决方式,这就引入了线程的同步

 

多线程存钱

 

/*
需求:
银行有一个金库。
有两个储户分别存300员,每次存100,存3次。

目的:该程序是否有安全问题,如果有,如何解决?


如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
*/
class Banke
{
	//累加存储功能
	private int sum;//多线程共享数据
	public synchronized void add(int i)//多线程共同执行的语句,避免多线程安全
	{
		sum=sum+i;
		System.out.println(Thread.currentThread()+"sum"+sum);
	}
}
class Cus implements Runnable
{
	private Banke b = new Banke();//定义对象操作Banke类的add方法
	public void run()
	{	
		for(int i=0;i<3;i++)
		{
			b.add(100);
		}
	}
}
public class Bank1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Cus c=new Cus();
		
		Thread t1=new Thread(c);
		Thread t2=new Thread(c);
		t1.start();
		t2.start();
	}
}


 

 

 三、线程的同步

 

1、同步代码块

(1)格式:

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

}

对象:如同锁
持有锁的线程可以在同步中执行。
没有锁的线程即使获取CPU的执行权,也进不去,因为没有获取锁。

(2)同步的前提:
1:必须要有两个或者两个以上的线程。
2:必须是多个线程使用同一个锁。
3:必须保证同步中只能有一个线程在运行。
(3)好处:解决了多线程的安全问题。

         弊端:多个线程需要判断锁,较为消耗资源。

例如火车上的卫生间经典示例!

卫生间的门关上并锁死,表示里面有人使用,外面的人进不来,只能等里面的人使用完出来才能进入;

卫生间的门开着,表示里面没有人,外面的人可以进来使用,进来一个人把门关上锁死后,外面的人就又进不来了。

 

2、同步函数

同步函数就是在方法前面修饰synchronized关键字的方法。

当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能执行,必须将每个能访问共享资源的方法修饰为同步方法,否则会出错。

 

3.同步函数和同步代码块的区别?

 

      同步函数简化了代码书写,减少了代码的缩进,但是同步代码块更灵活,可以嵌套在方法体中

 

 

4.小知识点:

若在run方法前加同步关键字,则为单线程

同步函数的锁是this

静态同步函数的锁,使用的是Class对象作为锁  类名.class

 

5.单例设计模式----懒汉式

懒汉式 ---延迟加载

懒汉式 ---延迟加载
  class  Single
  {
 		private static Single s=null;
  		private Single(){}
  		public static void getInstance()
 		{
 			if(s==null)
 				{	
 					synchronized(Single.class)
 					{
 						if(s==null)
 						s=new Single();
 					}
 				}
 			return s;//这一步经常忘记
 		}


 

记住懒汉式常问的问题!还有为什么懒汉式叫延迟加载?

1.懒汉式和饿汉式有什么不同:懒汉式 是实例的延迟加载

2.懒汉式有安全隐患吗?多线程访问的时,会出现多个实例的安全问题

3.安全隐患解决办法:加同步代码块或者同步函数,但是比较低效,可以使用双重判断+内部同步代码块的方式解决低效的问题

4.懒汉式的锁是:该类所属的字节码对象

 

6.死锁示例

 

死锁是指发生在线程之间相互阻塞的现象,这种现象导致同步线程相互等待,以致每个线程都不能往下执行。发生在同步的嵌套中。

 

下面写一个简单的死锁

<span style="font-size:18px;">class Test implements Runnable
{
	boolean flag;
	public Test(boolean b) 
	{
		this.flag=b;
	}
	public void run()
	{
		
			if(flag)
			{
				while(true)
				{
					synchronized (Lock.locka) 
					{  
						System.out.println(Thread.currentThread()+"locka if!");
						synchronized(Lock.lockb)
						{
							System.out.println(Thread.currentThread()+"lockb if!");
						}
					}
				}
			}
			else
			{
				while(true)
				{
					synchronized (Lock.lockb) 
					{  
						System.out.println(Thread.currentThread()+"locka else!");
						synchronized(Lock.locka)
						{
							System.out.println(Thread.currentThread()+"lockb else!");
						}
					}
				}
			}
		}
	}
	


//定义锁</span>
<span style="font-size:18px;">class Lock
</span>
<span style="font-size:18px;">{
	static Object locka=new Object();
	static Object lockb=new Object();
}
public class DeadLock {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
//定义线程并且启动</span>
<span style="font-size:18px;">		 Thread s1=new Thread(new Test(true));
		 Thread s2=new Thread(new Test(false));
		 s1.start();
		 s2.start();
	}

}</span>


死锁问题发生:

线程1有a锁,线程2有b锁;线程1想要访问b锁,线程2想要访问a锁,双方还不放自己的锁,于是产生死锁。

 

 

 

易引起 是因为两个锁引用的不是同一个锁,这样就会引起死锁现在 

          还有在多现在中出现安全性问题的时候,一般要考虑的问题: 

       1.   是不是两个线程,并且两个线程有没有同步,也就是有没有 synchronized 块或函数 

       2.   如果有多个同步代码块或同步函数的话,那看看所有 的同步它们用的是不是同一个锁,如果不是,会引起死锁和安全性问题 

 

 四.多线程的操作【难不理解】

 

1、线程通信

(1)线程通信

多线程一个重要特点就是它们之间可以相互通信,线程通信是线程之间可以相互交流和等待,可以通过经常共享的数据使线程相互交流,也可以通过线程控制方法使线程之间相互等待。

线程间通信:其实就是多个线程在操作同一个资源,但是操作的动作不同。

(2)等待唤醒机制

线程通信可以通过线程控制方法使线程互相等待。

Object类提供了3个方法:wait()、notify()和notifyAll()。

都使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用在同步中,因为只有同步才具有锁。


为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。


wait()和sleep()的区别:wait() 释放资源,释放锁;sleep()  释放资源,不释放锁。

 

五.线程小知识点

1.停止线程,守护线程【守护线程就是后台线程】,join方法,yield()方法

Interrupt()

setDaemon(boolean on)该方法必须在启动前调用

后台线程的特点:开启后,和前台线程共同抢夺cpu执行权,当所有的前提线程都结束后,后台线程会自动结束

Join方法;等待该线程停止,即,cpu的执行权在调用join方法的线程手里,而等这个线程执行完毕之后,线程.join之后的线程交互执行,

 2.线程池

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。

 

 

3.Java线程中sleep和wait的区别,

java 线程中的sleep和wait有一个共同作用,停止当前线程任务运行,但他们存在一定的不同,首先我们先看sleep中的构造函数。

  1、这两个方法来自不同的类分别是Thread和Object

  2、最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。

  3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在

  任何地方使用(使用范围)

  synchronized(x){

  x.notify()

  //或者wait()

  }

  4、sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常

 

 

 

 

 

 

 

 

 

 

 

    

 

 

本项目是一个基于SSM(Spring+SpringMVC+MyBatis)后端框架与Vue.js前端框架开发的疫情居家办公系统。该系统旨在为居家办公的员工提供一个高效、便捷的工作环境,同时帮助企业更好地管理远程工作流程。项目包含了完整的数据库设计、前后端代码实现以及详细的文档说明,非常适合计算机相关专业的毕设学生和需要进行项目实战练习的Java学习者。 系统的核心功能包括用户管理、任务分配、进度跟踪、文件共享和在线沟通等。用户管理模块允许管理员创建和管理用户账户,分配不同的权限。任务分配模块使项目经理能够轻松地分配任务给团队成员,并设置截止日期。进度跟踪模块允许员工实时更新他们的工作状态,确保项目按计划进行。文件共享模块提供了一个安全的平台,让团队成员可以共享和协作处理文档。在线沟通模块则支持即时消息和视频会议,以增强团队之间的沟通效率。 技术栈方面,后端采用了Spring框架来管理业务逻辑,SpringMVC用于构建Web应用程序,MyBatis作为ORM框架简化数据库操作。前端则使用Vue.js来实现动态用户界面,搭配Vue Router进行页面导航,以及Vuex进行状态管理。数据库选用MySQL,确保数据的安全性和可靠性。 该项目不仅提供了一个完整的技术实现示例,还为开发者留下了扩展和改进的空间,可以根据实际需求添加新功能或优化现有功能。
本项目是一个基于SSM(Spring+SpringMVC+MyBatis)后端框架与Vue.js前端框架开发的网上球鞋竞拍系统。该项目旨在为球鞋爱好者提供一个便捷、高效的在线竞拍平台,用户可以在此平台上浏览、搜索、竞拍心仪的球鞋,并参与到各种有趣的竞拍活动中。 系统的主要功能包括用户注册登录、球鞋信息展示、竞拍活动创建与管理、实时竞拍以及交易安全保障等。用户可以通过注册账号后,浏览平台上发布的各类球鞋信息,包括品牌、型号、颜色、尺码以及当前竞拍状态等。系统支持用户创建和管理自己的竞拍活动,设定竞拍规则和时间,同时提供实时竞拍功能,确保公平、透明的交易过程。 在技术实现上,后端采用SSM框架进行开发,Spring负责业务逻辑层,SpringMVC处理Web请求,MyBatis进行数据库操作,保证了系统的稳定性和扩展性。前端则使用Vue.js框架,结合Axios进行数据请求,实现了前后端分离,提高了开发效率和用户体验。 数据库设计方面,系统采用了MySQL数据库,存储用户信息、球鞋信息、竞拍活动等数据,确保数据的安全性和完整性。此外,项目还包含了详细的文档资料,包括需求分析、系统设计、数据库设计以及测试报告等,为项目的实施和维护提供了有力的支持。 该项目不仅适合作为计算机相关专业学生的毕业设计题目,也适合Java学习者进行实战练习,通过在此基础上进行功能扩展和改进,可以进一步提升编程技能和项目管理能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值