Java中的多线程

什么是进程?

进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。

什么是线程

每个运行的程序都是一个进程,在一个进程中还可以有多个执行单元同时运行,这些执行单元可以看作程序执行的一条条线索,被称为线程。

线程与进程的关系

线程是CPU调度的最小单元,同时线程是一种有限的系统资源,即线程不可能无限制地产生,并且线程的创建和销毁都有相应的开销。而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。

  • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
  • 资源分配给进程,同一进程的所有线程共享该进程的所有资源。
  • 处理机分给线程,即真正在处理机上运行的是线程。
  • 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
  • 线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

多线程的实现方案

一丶继承Thread类,重写run()方法

  • 定义一个类继承Thread类
  • 覆盖Thread类中的run方法
  • 直接创建Thread的子类对象创建线程
  • 调用start方法开启线程并调用线程的任务run方法执行

二丶实现Runnable接口

  • 定义类实现Runnable接口
  • 覆盖接口中的run方法,将线程的任务代码封装到run方法中
  • 通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务
  • 调用线程对象的start方法开启线程

run()和start()方法的区别

  • run():仅仅是封装了被线程执行的代码,直接调用就是普通方法
  • start():首先是启动了线程,然后再由jvm去调用了该线程的run()方法,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。

Thread和Runnable的区别

  • Thread单继承,Runnable可以多实现
  • Runnable可以实现资源共享
  • Thread定义线程和使用线程在一起
  • Runnable定义线程和使用线程分开,解耦

同步和死锁

为什么要同步?

几个对象都要用同一个资源, 需要共享数据,Java线程共享数据需要同步的根本原因在于Java内存的设计。

如何同步?实现同步的方法?

同步的内部机制:互斥锁
关键字:synchronized

同步代码块:

在代码块声明上加上synchronized
 synchronized (锁对象) {
 可能会产生线程安全问题的代码
 }同步方法:在方法声明上加上synchronized
 public synchronized void method(){
 可能会产生线程安全问题的代码
 }

死锁

死锁:
同步锁使用的弊端:当线程任务中出现了多个同步(多个锁)时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。

synchronzied(A锁){
 synchronized(B锁){}


}过多的同步就有可能导致死锁