博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 多线程回顾
阅读量:6812 次
发布时间:2019-06-26

本文共 3899 字,大约阅读时间需要 12 分钟。

  hot3.png

做java web开发,一直以来比较依赖java框架和oracle数据库的功能。因为一般遇到高并发的情况并不多,企业内软件多半用户数不多,即使偶尔遇到,也都在oracle数据库中处理了。

对java的多线程开发,一直以来只有一个简单的概念,没有深入使用理解过,希望这次利用网上的内容,能够回顾一下。

多线程相关概念

进程:一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。

线程:进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。

操作系统中的进程是资源的组织单位。进程有一个包含了程序内容和数据的地址空间,以及其它的资源,包括打开的文件、子进程和信号处理器等。不同进程的地址空间是互相隔离的。而线程表示的是程序的执行流程,是CPU调度的基本单位。线程有自己的程序计数器、寄存器、栈和帧等。引入线程的动机在于操作系统中阻塞式I/O的存在。当一个线程所执行的I/O被阻塞的时候,同一进程中的其它线程可以使用CPU来进行计算。这样的话,就提高了应用的执行效率。线程的概念在主流的操作系统和编程语言中都得到了支持。

线程与进程的区别:

  • 地址空间: 进程内的一个执行单元;进程至少有一个线程,它们共享进程的地址空间;而进程有自己独立的地址空间;

  • 资源拥有: 进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源

  • 线程是处理器调度的基本单位,但进程不是.

  • 二者均可并发执行.

线程相关的状态以及转换

线程共有下面4种状态:

  • 新建状态(New):新创建了一个线程对象,当你用new创建一个线程时,该线程尚未运行。

  • 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

  • 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

  • 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

    等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

    同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM把该线程放入锁。

    其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

  • 死亡状态(Dead)

    由于run方法的正常退出而自然死亡;

    没有捕获到的异常事件终止了run方法的执行,从而导致线程突然死亡

状态之间的转换可以用下面的两张图来展示

thread color

thread detail

多线程实现方法

  • 继承Thread类

    public class TestThread extends Thread { @Override public void run() { super.run(); for (int i = 0; i < 1000; i++) { System.out.println(“T-“ + i);

    try          {              Thread.sleep(800);          } catch (InterruptedException e)          {              e.printStackTrace();          }      }  }   }
  • 实现Runnable接口

    public class TestRunnable implements Runnable {

    @Override  public void run()  {      for (int i = 0; i < 1000; i++)      {          System.out.println("R-" + i);          try          {              Thread.sleep(1000);          } catch (InterruptedException e)          {              e.printStackTrace();          }      }  }  public static void main(String[] args)  {      Thread runnable = new Thread(new TestRunnable());      Thread thread = new TestThread();      thread.start();      runnable.start();  }   }

运行结果:

thread console result

线程的分类

  • 守护线程(daemon threads)

守护线程通常为普通线程提供服务,它们通常有一个无限循环,等待服务请求或执行线程的任务,在没有其他的非守护线程运行时,守护线程将停止工作。即使你不创建任何线程,Java应用程序在默认情况下创建多个线程。他们大多是守护线程,主要用于处理任务,如垃圾收集或JMX。

守护线程的特点:

  1. 优先级非常低

  2. 只有当同一个程序的任何其他线程正在运行时执行。

  3. 当线程只剩下守护线程的时候,JVM就会退出.但是如果还有其他的任意一个用户线程还在,JVM就不会退出.

  • 非守护线程 (non-daemon threads)或普通线程或用户线程(user threads)

用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了

在JAVA中通过调方法Thread.setDaemon(true)将线程转为守护线程。但需要注意的有:

  1. 但必须在调Thread.start()方法之前,因为一旦线程正在运行,你不能修改它的守护进程的状态。

  2. 在Daemon线程中产生的新线程也是Daemon的

  3. 守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

代码示例:

public class DaemonThread{	public static void main(String[] args)	{		Thread thread = new Thread(new ThreadRunnable());		thread.setDaemon(true);		thread.start();		try		{			Thread.sleep(750);		} catch (InterruptedException e)		{			e.printStackTrace();		}		System.out.println("Main Thread ending");	}}class ThreadRunnable implements Runnable{	@Override	public void run()	{		int count = 0;		while (true)		{			System.out.println("Hello from Worker " + count++);			try			{				Thread.sleep(350);			} catch (InterruptedException e)			{				e.printStackTrace();			}		}	}}

运行结果:

Hello from Worker 0Hello from Worker 1Hello from Worker 2Main Thread ending

从运行结果可以看到,随着主线程的结果,本来是无限循环的子线程,也跟着结束了,说明守护线程不会影响程序的结束。

线程状态的转换

线程的状态转换,主要是由sleep()、wait()、yeid()、join()等几个方法来实现(stop()、interrupt()等方法貌似都有一定的问题,不是很推荐使用)。

sleep方法与wait方法的区别:

sleep方法是静态方法,wait方法是非静态方法。sleep方法在时间到后会自己“醒来”,但wait不能,必须由其它线程通过notify(All)方法让它“醒来”。sleep方法通常用在不需要等待资源情况下的阻塞,像等待线程、数据库连接的情况一般用wait。

sleep/wait与yeld方法的区别:

调用sleep或wait方法后,线程即进入block状态,而调用yeld方法后,线程进入runnable状态。

wait与join方法的区别:

wait方法体现了线程之间的互斥关系,而join方法体现了线程之间的同步关系。wait方法必须由其它线程来解锁,而join方法不需要,只要被等待线程执行完毕,当前线程自动变为就绪。join方法的一个用途就是让子线程在完成业务逻辑执行之前,主线程一直等待直到所有子线程执行完毕。

转载于:https://my.oschina.net/liting/blog/535279

你可能感兴趣的文章
ListView下拉刷新实现(类似陌陌的箭头转动)
查看>>
[Android编程心得] Camera(OpenCV)自动对焦和触摸对焦的实现
查看>>
Android apk的安装、卸载、更新升级(通过Eclipse实现静默安装)
查看>>
关于新建android项目时 appcompat_v7报错问题的一点总结
查看>>
基于数据库复制的技术架构讨论
查看>>
jQuery学习笔记(一)
查看>>
第一课 矩阵的行图像与列图像(麻省理工公开课:线性代数)【转载】
查看>>
Screen Orientation for Windows Phone
查看>>
程序1:四则运算
查看>>
常用的排序算法的时间复杂度和空间复杂度
查看>>
找不到或无法加载主类
查看>>
通配符&&选择器
查看>>
2013与2014之流水
查看>>
call Kernelized Correlation Filters Tracker(Matab) in Qt(c++)
查看>>
CodeForces 292D Connected Components (并查集+YY)
查看>>
函数式编程
查看>>
为什么要有mmu
查看>>
1018. Binary Prefix Divisible By 5可被 5 整除的二进制前缀
查看>>
webform 组合查询
查看>>
java多态 -- 猫狗案列
查看>>