Java中的多线程

一、进程

1、这是操作系统中的概念,每个独立运行的程序就是一个进程

2、一个操作系统可以维护多个进程的同时运行,统一分配系统资源

3、一个操作系统可以维护多个进程,这个叫做:支持多进程。多进程的意义:

A、可以充分利用CPU的资源

B、为客户的使用提供了很好的支持,客户可以同时启动多个软件

 

二、线程

1、一个进程内部的一些代码块,可以以独立于此进程的方式单独运行。它可以与此进程同时竞争系统资源,这些在进程内部以独立的方式运行的代码就叫做线程。

2、意义:

A、可以提高程序的运行效率

B、可以提高CPU的利用率

 

三、多进程和多线程

1、多进程:

相对于操作系统,可以同时运行多个应用程序

2、多线程

一个主进程可以开出多个线程单独运行

3、单线程

只有一条执行路径

 

四、并行和并发

1、并行:在某个时间段内,两个线程同时运行

2、并发:在某个时间点上,两个线程同时运行。尤其值多个线程同时访问同一资源时

 

五、java中多线程的创建和使用

1、创建子线程

1/1方式一:继承Thread

①线程在类库中被封装为Thread类,Thread类就是程序中执行的线程

②创建步骤

1)创建一个类,该类继承Thread

2)在该类中重写Threadrun()方法,方法中写想要单独执行的代码

3)创建Thread实现类的对象

4)实现类的对象调用对象的start()方法。start()继承自Thread类其中调用了run()方法

启动线程只能调用start()方法,因为虽然run()方法实现了多个线程的并行处理,但不能直接调用run()方法,而是通过start()方法来调用。在调用start()的时候,start方法会首先进行与多线程相关的初始化,然后再自动调用run()方法。
start()方法
– 线程调用该方法启动一个线程,使之从新建状态进入就绪队列排队,至于什么时候开始执行线程中的内容需要等待分配资源,一旦轮到它来使用CPU资源时,就可以脱离创建它的主线程独立开始自己的生命周期。
run()方法
– 线程的所有活动都是通过线程体run()方法来定义并实现它被调用以后所执行的操作。

③注意:

1)一个子线程只能被start()一次

2)不能通过对象直接调用run()方法启动线程

④为什么调用start()方法启动线程(伪代码)

				class Thread{
					public void run(){
					       System.out.println("a");
					}
					public void start(){//子类重写Thread的run方法后会调用子类的run
					       run();
					}
				}
				class MyThread extends Thread{
					public void run(){
					       System,out.println("b");
					}
				}



 

1/2方式二:实现Runnable接口

1)创建一个类,该类实现Runnable接口

2)在该类中重写run()方法,该方法中实现子线程的功能

3)创建该类的对象

4)创建Thread类对象的引用,将所创建的类的对象作为形参传递给Thread类的构造器

5Thread类对象的引用调用start()方法启动线程

 

1/3实现方式三:实现Callable接口(JDK 5

1)创建一个类,实现Callable接口

2)重写call()方法

3)获取一个线程池

4)调用线程池的submit()方法启动线程,将实现类的对象作为参数传递进去

5)关闭线程

 

1/4继承thread类与实现Runnable接口的比较

1Thread类也实现了Runable接口,class Thread implements Runable{}

2)实现Runable接口的方式优于继承Thread类的方式

原因:①避免了java单继承的局限性

 ②如果多个线程操作同一份资源(数据),实现Runnable接口的方式更合适

 

2、线程中的常见方法

2/1 getName()获取线程的名称

2/2 currentThread()获取当前线程

2/3 setName()设置线程的名称

2/4 run()子线程要执行的代码放在run()方法中

2/5 start()启动线程

2/6 setPriority(()设置线程的优先级

2/6 getPriority()获取线程的优先级

2/7yield()当前线程释放CPU资源,退回到就绪状态,同其他线程一起等待操作系统分配资源。释放后可能还会抢回资源yield以后可能会马上           

       又抢回资源,而且不保证一定是此线程最后执行完成

2/8 join()A线程中调用B线程的join()方法,表示当执行到此方法时A线程停止执行,直到B线程执行完毕,A线程在继续执行join()的进程与在其之前已经启动的线程会同时执行,其后的线程会在其执行完成后再执行。其后的线程与其之前的线程会同时执行

2/9isAlive()判断当前线程是否存活

2/10 sleep(long l)显示的让线程沉睡一段时间

2/11 Thread.setDeamon(true):设置守护线程

2/12 wait():让当前访问的线程等待,只能在同步中使用

2/13 notify():唤醒线程,只能在同步中使用

2/14 notifyAll()唤醒线程,只能在同步中使用

 

六、线程的优先级:

1java使用抢占式调度模型

2、优先级的范围是1---10

3、默认的优先级为5

4、可以通过setPriority()设置线程的优先级

      可以通过getPriority()获取线程的优先级

5、线程的优先级依赖于操作系统的调度,需要跟操作系统配合,不能保证优先级高的线程一定先执行完毕,所以不能依赖于使用线程的优先级试图让某个线程先执行完毕

6、如果线程内部的业务逻辑过于简单,线程的优先级将看不出效果

 

七、多线程的优点

1、提高应用程序的响应,对图形化界面更有意义,可增加用户体验

2、提高计算机系统CPU的利用率

3、改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

 

八、线程的分类

1、非守护线程:

          当主方法结束时,主线程开出的线程仍在继续运行,直到运行完成,这个就是非守护线程

2、守护线程

2/1当主进程结束时,守护线程也跟着结束,但会有一点缓冲,不会立马结束

2/2守护线程是用来服务用户线程的,通过在调用start()方法前调用Thread.setDeamon(true)方法可以将用户线程变成守护线程。

2/3垃圾回收是一个典型的守护线程

2/4JVM中只有守护线程,则JVM退出

 

九、线程的中断

1、可以在主进程汇总结束某个线程

2、中断方式:

方式一:使用stop()方法

已过时,不安全

方式二:interrupt:当线程处于:Object--wait()Thread--join()以及Thread---sleep()受阻时,此方法调用将会导致上述三个方法阻塞时的异常抛出

十、线程的生命周期

1、新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态

2、就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件

3、运行:当就绪的线程被调度并获得处理器资源时,便进入运行状态, run()方法定义了线程的操作和功能

4、阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU并临时中止自己的执行,进入阻塞状态

5、死亡:线程完成了它的全部工作或线程被提前强制性地中止  

 


十一、线程安全

1、线程安全存在的原因

由于一个线程在操作共享数据的过程中,为执行完毕的情况下,另一个线程参与进来,导致共享数据出现了安全问题

2、解决线程的安全问题

必须让一个线程操作共享数据完毕后,才允许其他线程参与进来

3、实现线程的安全,线程的同步机制

方式一、同步代码块

synchronized(同步监视器){

//需要被同步的代码块(即操作共享数据代码)

}

共享数据:多个线程共同操作的同一个数据(一个变量)

同步监视器:由任何一个类的对象充当。获取此监视器的线程执行大括号中被同步的代码。俗称:锁

要求所有的线程必须共用同一把锁

方式二、同步方法

			public synchronized void  show(String name){
				//代码
			}


4、线程同步的弊端:由于同一个时间只能有一个线程访问共享数据,所以效率降低了

5、静态方法中的同步代码块不能锁this,一般锁这个类的class对象

静态进内存时,内存中没有本类对象,但是一定有该类的字节码文件对象类名.class  该对象的类型是class 


		public static void show(){
			synchronized (Accound.class) {
				System.out.println("show()");
			}
		}

      静态方法可以是synchronized

		public synchronized static void show2(){
			System.out.println("show2()");
		}


十二、锁的释放

1、释放锁的操作

当前线程的同步方法、同步代码块执行结束

当前线程在同步代码块、同步方法中遇到breakreturn终止了该代码块、该方法的继续执行。

当前线程在同步代码块、同步方法中出现了未处理的ErrorException,导致异常结束

当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。

2、不会释放锁的操作

线程执行同步代码块或同步方法时,程序调用Thread.sleep()Thread.yield()方法暂停当前线程的执行

线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。

应尽量避免使用suspend()resume()来控制线程

 

十三、JDK5之后出现的Lock锁(Lock接口)

1Lock实现提供了比使用 synchronized方法和语句可获得的更广泛的锁定操作。

2、使用方法:

Lock l = ...; //创建一个子类对象(直接子类;ReentrantLock,ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock)
l.lock();//加锁
try {
    // 被同步的代码
} finally {
   l.unlock();//释放锁
}


 

十四、死锁

定义:

不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

解决方法:

专门的算法、原则

尽量减少同步资源的定义

 

 

十五、线程通信

1wait()notify()notifyAll()

wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问

notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待

notifyAll ():唤醒正在排队等待资源的所有线程结束等待.

Java.lang.Object提供的这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常

 

2wait()方法

2/1在当前线程中调用方法: 对象名.wait()

2/2使当前线程进入等待(某对象)状态,直到另一线程对该对象发出 notify (notifyAll)为止。

2/3调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)

2/4调用此方法后,当前线程将释放对象监控权 ,然后进入等待

2/5在当前线程被notify后,要重新获得监控权,然后从断点处继续代码的执行。

3notify()/notifyAll()

在当前线程中调用方法: 对象名.notify()

功能:唤醒等待该对象监控权的一个线程。

调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)

 

十六、线程组(ThreadGroup)

1、默认线程组:

所有线程都默认属于一个线程组:main线程组

2、可以将一些线程放到一个指定的线程组内

2/1构造方法

ThreadGroup(String name):创建一个指定名称的线程组

2/2将线程放到线程组的步骤

1、创建一个线程组

2、实例化线程对象

3、实例化一个Thread,使用自定义线程组、线程、线程名称做参数

3、我们可以对一个线程组中的线程进行统一操作,比如停止线程组中的所有线程

 

十七、线程池(JDK 5之后)

1、对于同一个Thread对象,不能重复调用start()方法

2、若想再次使用这个Thread对象,只能重新创建一个对象再调用

3、这种情况下可以使用线程池

1/1他可以缓存已经执行过的线程

1/2对于存在与缓存池中的线程可以直接执行

1/3 Executors

1/3/1成员方法:

public static ExecutorService newCachedThreadPool():创建一个可根据需要创建新线程的线程池,但是在以前构

造的线程可用时将重用它们。

public static ExecutorService newFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池,

public static ExecutorService newSingleThreadExecutor():创建一个使用单个 worker 线程的Executor,以无界队列

方式来运行该线  程。新创建的单线程 Executor

返回值:ExecutorService()

Future<?> submit(Runnable task):执行并缓存线程对象;

停止线程:shutdown();

4、创建步骤

1、创建一个线程池

ExecutorService service = Executors.newFixedThreadPool(2);

2、创建线程类对象

MyThread t4 = new MyThread();

MyThread t5 = new MyThread();

3、使用线程池的submit()方法启动线程,将线程对象作为参数传递给submit()方法

service.submit(t4);//执行t4并缓存t4

service.submit(t5);//执行并缓存t5

4、关闭线程池的方法

 1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

2. 使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。
3. 使用interrupt方法中断线程。 

十八、匿名内部类的方式实现多线程

1new Thred(){匿名的Thread的子类}

2new Thread(){匿名的Runnable的子类}

3new Thread(匿名的Runnable的子类){匿名的Thread的子类},最终执行Thread子类的run()方法

 

	public class Demo {
		public static void main(String[] args) {
			//1.new Thread(){匿名的Thread的子类};
			new Thread(){
				//重写run()
				@Override
				public void run() {
					System.out.println("a");
				}
			}.start();
			
			//2.new Thread(匿名的Runnable的子类){};
			new Thread(new Runnable(){
				@Override
				public void run() {
					System.out.println("b");
				}
			}){}.start();
			
			//3.new Thread(匿名的Runnable的子类){匿名的Thread的子类};
			new Thread(new Runnable(){
				@Override
				public void run() {
					System.out.println("c");
				}
			}){
				@Override
				public void run() {
					System.out.println("d");//最终执行的是子类的run()
				}
			}.start();
		}
	}

 

十九、定时器

1、在指定的时间,完成指定的任务

2、在指定的时间开始,每个多长时间,重复的执行指定的任务

3、定时器的实现

3/1使用到的类:TimerTaskTimer

3/1/1 TimerTask:描述具体的任务

①定义类,继承自TimerTask;

②重写run()方法,将具体的任务写到这里

3/1/2 Timer:启动TimerTask任务

①构造一个Timer对象,使用无参构造方法

②调用方法

public void schedule(TimerTask task,longdelay):在指定的delay时间后,执行task任务,只执行一次

public void schedule(TimerTask task,longdelay,long period):在指定的delay时间后,执行task任务,每隔

period时间执行一次

二十、wait()/sleep()/yield()的区别

wait()Object类的方法,会释放锁,可以指定时间,使用notif()或者notifyAll()唤醒

sleep()Thread类的方法,不会释放锁,必须指定时间,时间到后会自动唤醒

yield()Thread类的方法,不会释放锁,使当前访问的线程退回到就绪状态,但是,当在"同步"方法中使用时,不会释放锁;

 

二十一、面试题

1.多线程有几种实现方案,分别是哪几种?三种

                 1).继承Thread类,重写run()方法

<span style="white-space:pre">		</span>class MyThread extends Thread{
 			public void run(){
			}
 		}
		main(){
 		<span style="white-space:pre">	</span>MyThread t = new MyTrhead();
  			t.start();
 		}



                 2).实现Runnable接口,重写run()方法

<span style="white-space:pre">		</span>class MyRunnable implements Runnable{
  			public void run(){
  			}
  		}
  		main(){
 			MyRunnable myRun = new MyRunnable();
  			Thread t = new Thread(myRun);
  			t.start();
  		}

                 3).实现Callable接口,重写:call()方法

<span style="white-space:pre">		</span>class MyCallable implements Callable{
  			public Object call() throws Exception{
  				return null;
  			}
  		}
  		main(){
  			//1.获取一个线程池
  			ExecutorService service = Executors.newFixedThreadPool(2);
  			//2.执行submit()方法:
  			Future result =  service.submit(new MyCallable());
  			//获取返回值
  			Object value = result.get();
  			service.shutdown();
 		}

         

 2.同步有几种方式,分别是什么?

  1).同步代码块:

synchronized(被锁的对象){

                                 

}

被锁的对象:如果有一个线程访问此对象的某个同步代码块时,意味着这个线程当前锁住了整个对象,其它线程无法访问此对象内的所有同步的代码块及同步方法。

A.在某个类中的同步代码块,通常锁的对象设定为:this;

2).同步方法:

public synchronized void show(){//锁的对象,默认为当前对象;

}

静态的方法内可以包含"同步代码块",一般锁的对象是当前类的class对象。

静态方法也可以是"同步的"

    

 3.启动一个线程是run()还是start()?它们的区别?

start()方法。

区别:

1.run():重写父类的方法,需要线程执行的内容,写到这里;

2.start():启动线程,内部会自动调用run();

 

 4.sleep()wait()方法的区别:

1.sleep():Thread类的方法;在同步方法内,不释放锁;必须指定时间,当时间到时,会自动醒来;

2.wait():Object类的方法;在同步方法内,会释放锁;可以指定时间,也可以不指定时间。使用notify()notifyAll()唤醒,或者时间到了,自动醒来;

 

 5.为什么wait(),notify(),notifyAll()等方法都定义在Object类中:

任何对象都可能会被多个线程并发访问,所以要求任何对象都有wait()notify()的功能,所以就定义在了Object类中。

 

6.线程的生命周期图:

基本的声明周期:新建-->就绪-->运行-->死亡

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值