java 中的多线程

简介

  进程 : 指正在运行的程序,并具有一定的独立能力,即 当硬盘中的程序进入到内存中运行时,就变成了一个进程

  线程 : 是进程中的一个执行单元,负责当前程序的执行。线程就是CPU通向程序的路径

       一个进程中只有一个线程,单线程程序

       一个进程中是可以有多个线程的,这个应用程序是多线程程序

程序的运行分类

  分时调度

    所有线程轮流使用CPU 的使用权,平均分配每个线程占用CPU 的时间

  抢占式调度

    优先让优先级高的线程使用CPU,如果线程的优先级相同,那么就会随机选择一个线程(线程的随机性)。

    java 使用的为抢占式调度

   抢占式调度简介:

    现在的操作系统都支持多进程并发运行,比如:一边用office ,一边使用QQ,一边看着视频  等等,

    看着好像这些程序都在同一时刻运行,实际上是 CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。

    对于CPU的一个核而言,某个时刻只能执行一个线程,而CPU 在多个线程之间切换的速度相对我们而言感觉要快,看上去就是在同一时刻运行。

    注意:

      多线程并不能提高程序的运行速度,但能够提高程序的运行效率,让CPU 的使用效率更高。

多线程的由来  

  jvm启动后,必然有一个执行线程(路径)从main方法开始的,一直执行到main方法结束,这个线程在java中称之为主线程(main线程)。 

  若主线程遇到循环,并循环次数较多,则导致程序在指定的位置停留时间过长,无法马上执行下面的程序,则需要等待循环结束后才能够执行代码。效率慢。 

  主线程负责执行其中的一个循环,由另一个线程执行另一个循环,最终实现多部分代码同时执行。多线程之间互不影响。

多线程的创建方式

  1、继承 Thread 类

    创建一个类,继承 Thread 类,并重写Thread 类的 run 方法

    创建对象,调用 run 方法 ,就相当于执行其他线程的 main 方法。

    步骤:

      1、自定义一个类,继承Thread 类

      2、重写Thread 类中的 run 方法 ,设置线程任务

      3、创建自定义类的实例

      4、实例化 Thread 类,并传入自定义的类

      4、调用start ,开启进程。

    示例:

  1. 1、自定义类
  2. // 创建一个类,继承Thread
  3. public class Thread_01 extends Thread{
  4. // 重写run 方法,在run方法中定义线程任务
  5. public void run(){
  6. System.out.println(Thread.currentThread().getName());
  7. }
  8. }
  9. 2main方法
  10. public class ThreadDemo {
  11. public static void main(String[] args) {
  12. // 创建线程对象 t1
  13. Thread_01 t1 = new Thread_01();
  14. // 为了启动Thread_01 这个线程,需要实例化Thread,并传入自己的Thread_01实例
  15. Thread thread = new Thread(t1);
  16. // 通知CPU 要启动线程
  17. thread.start();
  18. System.out.println(Thread.currentThread().getName());
  19. }
  20. }

多线程继承Thread 示例

2、实现 Runnable 接口

    创建一个类,实现 Runnable 接口,重写 run  方法

    步骤:

      1、自定义一个类,实现 Runnable 接口

      2、重写run 方法,在run方法中设置线程任务

      3、在main 方法中,创建自定义类的实例化

      4、实例化Thread 类,并传入自定义类的实例化

      5、调用start 方法,开启进程

  1. 1、自定义类,实现runnable 接口
  2. // 自定义类,实现Runnable 接口
  3. public class Runnable_01 implements Runnable{
  4. // 重写run 方法,在run方法中设置线程任务
  5. @Override
  6. public void run() {
  7. System.out.println(Thread.currentThread().getName());
  8. }
  9.  
  10. }
  11.  
  12. 2、在main方法中,调用
  13. public class ThreadDemo {
  14. public static void main(String[] args) {
  15. // 创建线程对象 t1
  16. Runnable_01 t1 = new Runnable_01();
  17. // 为了启动Runnable_01 这个线程,需要实例化Thread,并传入自己的Runnable_01实例
  18. Thread thread = new Thread(t1);
  19. // 通知CPU 要启动线程
  20. thread.start();
  21. System.out.println(Thread.currentThread().getName());
  22. }
  23. }

多线程实现 Runnable 接口

    注意:

      调用 start 方法,开启新线程。若没有调用start 方法,只调用run 方法,只是调用了一个方法而已,并没有开启新线程

      一个线程只能调用一次start 方法,若线程执行结束后,不能再调用。

常用API

  1. public class Demo {
  2. public static void main(String[] args) {
  3. // 调用currentThread().getName(),获取当前线程的名称
  4. System.out.println(Thread.currentThread().getName() + "123");
  5. // 调用currentThread().getId(),获取当前线程的标识符
  6. System.out.println(Thread.currentThread().getId());
  7. // 调用currentThread().getPriority(),获取当前线程的优先级
  8. System.out.println(Thread.currentThread().getPriority());
  9. // 调用currentThread().setName() 给当前线程设置新名称
  10. Thread.currentThread().setName("线程新名称");
  11. // 调用currentThread().getName(),获取当前线程的名称
  12. System.out.println(Thread.currentThread().getName() + "123");
  13. /**
  14. * 打印结果 :main123
  15. * 1
  16. * 5
  17. * 线程新名称123
  18. */
  19.  
  20. }
  21. }

线程API 使用示例

线程安全

  若多线程调用全局变量时,会出现线程安全问题。

  即:使用java 模拟窗口卖票时,一个窗口就是一个线程,若同时卖票,可能会出现几个窗口同时卖一张票,或者卖出不存在的票(就剩一张票时,两个窗口同时卖出)

  所以,使用多线程时,要注意线程安全问题,解决线程安全问题有三种方式,

  方式一:同步代码块

    同步代码块:就是在方法块声明上加上 synchronized

  1. synchronized (锁对象) {
  2. 可能会产生线程安全问题的代码
  3. }

  注意: 

    同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

    对象可以是this, 哪个对象调用,方法中的this就是哪个对象

  示例:

  1. /*
  2. * 开启3个线程,同时卖100张票
  3. */
  4. public class Demo01PayTicket {
  5. public static void main(String[] args) {
  6. //创建接口的实现类对象
  7. RunnableImpl r = new RunnableImpl();
  8. //创建线程对象
  9. Thread t0 = new Thread(r);
  10. Thread t1 = new Thread(r);
  11. Thread t2 = new Thread(r);
  12. //开启多线程
  13. t0.start();
  14. t1.start();
  15. t2.start();
  16. }
  17. }
  18.  
  19. /*
  20. * 发现程序出现了安全问题:卖出了重复的票和不存在的票
  21. *
  22. * 多线程安全问题的第一种解决方案:使用同步代码块
  23. *
  24. * 注意:
  25. * 代码块中传递的锁对象必须保证唯一,多个线程使用的是同一个锁对象
  26. * 锁对象可以是任意的对象
  27. *
  28. */
  29. public class RunnableImpl implements Runnable{
  30.  
  31. //定义一个共享的票源
  32. private int ticket = 100;
  33. //创建一个锁对象
  34. Object obj = new Object();
  35.  
  36. @Override
  37. public void run() {
  38. //让卖票重复执行
  39. while(true){
  40. //同步代码块
  41. synchronized (obj) {
  42. //判断是否还有票
  43. if(ticket>0){
  44.  
  45. //提高安全问题出现的概率,增加一个sleep
  46. try {
  47. Thread.sleep(10);
  48. } catch (InterruptedException e) {
  49. e.printStackTrace();
  50. }
  51.  
  52. //进行卖票
  53. System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
  54. ticket--;
  55. }
  56. }
  57. }
  58. }
  59. }

多线程 同步代码块

  方式二:同步方法

    1、同步方法:在方法声明上加上 synchronized

  1. public synchronized void method(){
  2. 可能会产生线程安全问题的代码
  3. } 

  同步方法中的锁对象是 this,哪个对象调用,方法中的this就是哪个对象

    2、静态同步方法:在方法声明上加上 static synchronized

  1. public static synchronized void method(){
  2.      可能会产生线程安全问题的代码
  3. }

  静态同步方法中的锁对象不是对象,是本类的 .class 文件

  示例:

  1. /*
  2. * 开启3个线程,同时卖100张票
  3. */
  4. public class Demo01PayTicket {
  5. public static void main(String[] args) {
  6. //创建接口的实现类对象
  7. RunnableImpl r = new RunnableImpl();
  8. //创建线程对象
  9. Thread t0 = new Thread(r);
  10. Thread t1 = new Thread(r);
  11. Thread t2 = new Thread(r);
  12. //开启多线程
  13. t0.start();
  14. t1.start();
  15. t2.start();
  16. }
  17. }
  18.  
  19. /*
  20. * 发现程序出现了安全问题:卖出了重复的票和不存在的票
  21. *
  22. * 多线程安全问题的第二种解决方案:使用同步方法
  23. *
  24. * 实现步骤:
  25. * 1.把访问了共享数据的代码提取出来放在一个方法中
  26. * 2.在方法上添加一个synchronized修饰符
  27. *
  28. * 格式:
  29. * 修饰符 synchronized 返回值类型 方法名(参数){
  30. * 访问了共享数据的代码;
  31. * }
  32. *
  33. * 把选中的代码提取到方法中快捷键:alt+shift+m
  34. *
  35. */
  36. public class RunnableImpl implements Runnable{
  37.  
  38. //定义一个共享的票源
  39. private static int ticket = 100;
  40.  
  41. @Override
  42. public void run() {
  43. //让卖票重复执行
  44. while(true){
  45. payTicketStatic();
  46. }
  47.  
  48. }
  49.  
  50. /*
  51. * 静态的同步方法,锁对象不是this
  52. * 静态优先于非静态加载到内存中,this是创建对象之后才有的
  53. * 锁对象是本类的class属性(反射-->class文件对象)
  54. */
  55. public static synchronized void payTicketStatic() {
  56. synchronized (RunnableImpl.class) {
  57. //判断是否还有票
  58. if(ticket>0){
  59. //提高安全问题出现的概率,增加一个sleep
  60. try {
  61. Thread.sleep(10);
  62. } catch (InterruptedException e) {
  63. e.printStackTrace();
  64. }
  65. //进行卖票
  66. System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
  67. ticket--;
  68. }
  69. }
  70. }
  71.  
  72. /*
  73. * 定义一个卖票的方法
  74. * 使用synchronized修饰
  75. * 使用锁对象把方法锁住
  76. * 这个锁对象是谁?
  77. * 创建的实现类对象new RunnableImpl();
  78. * 也就是this,哪个对象调用的方法,方法中的this就是哪个对象
  79. */
  80. public synchronized void payTicket() {
  81. //System.out.println(this);//cn.itcast.demo08.RunnableImpl@bcda2d
  82. synchronized (this) {
  83. //判断是否还有票
  84. if(ticket>0){
  85. //提高安全问题出现的概率,增加一个sleep
  86. try {
  87. Thread.sleep(10);
  88. } catch (InterruptedException e) {
  89. e.printStackTrace();
  90. }
  91.  
  92. //进行卖票
  93. System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
  94. ticket--;
  95. }
  96. }
  97. }
  98. }

多线程 同步方法 synchronized

  方式三:使用lock 锁

    Lock 是接口,  ReentrantLock 是Lock 的实现类

    API

      

    调用

      1、创建ReentrantLock 对象

      2、在可能产生安全问题代码前调用 lock() 方法,获得锁

      3、调用unlock()方法,解锁

    

  1.   ReentrantLock rl = new ReentrantLock();
  2.   //获得锁
  3.   rl.LOCK
  4.   可能会产生线程安全问题的代码
  5.   Rl.unlock

  示例:

  1. /*
  2. * 开启3个线程,同时卖100张票
  3. */
  4. public class Demo01PayTicket {
  5. public static void main(String[] args) {
  6. //创建接口的实现类对象
  7. RunnableImpl r = new RunnableImpl();
  8. //创建线程对象
  9. Thread t0 = new Thread(r);
  10. Thread t1 = new Thread(r);
  11. Thread t2 = new Thread(r);
  12. //开启多线程
  13. t0.start();
  14. t1.start();
  15. t2.start();
  16. }
  17. }
  18.  
  19. /*
  20. * 发现程序出现了安全问题:卖出了重复的票和不存在的票
  21. *
  22. * 多线程安全问题的第三种解决方案:使用Lock锁
  23. * java.util.concurrent.locks.Lock接口
  24. * Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
  25. * JDK1.5之后出现的新特性
  26. *
  27. * 实现步骤:
  28. * 1.在成员位置创建一个ReentrantLock对象
  29. * 2.在访问了共享数据的代码前,调用lock方法,获取锁对象
  30. * 3.在访问了共享数据的代码后,调用unlock方法,释放锁对象
  31. *
  32. */
  33. public class RunnableImpl implements Runnable{
  34.  
  35. //定义一个共享的票源
  36. private int ticket = 100;
  37. //1.在成员位置创建一个ReentrantLock对象
  38. Lock l = new ReentrantLock();
  39.  
  40. @Override
  41. public void run() {
  42. //让卖票重复执行
  43. while(true){
  44. //2.在访问了共享数据的代码前,调用lock方法,获取锁对象
  45. l.lock();
  46. try {
  47. //可能会出现安全问题的代码
  48. //判断是否还有票
  49. if(ticket>0){
  50. //提高安全问题出现的概率,增加一个sleep
  51. Thread.sleep(10);
  52. //进行卖票
  53. System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
  54. ticket--;
  55. }
  56.  
  57. } catch (Exception e) {
  58. //异常的处理逻辑
  59. System.out.println(e);
  60. } finally {
  61. //一定会执行的代码,资源释放
  62. //3.在访问了共享数据的代码后,调用unlock方法,释放锁对象
  63. l.unlock();//无论是否异常,都会释放掉锁对象
  64. }
  65. }
  66.  
  67. }
  68. }

多线程 lock 锁 示例

多线程线程图

  线程的五种状态:

    新建状态-->运行状态-->死亡(结束)状态

    阻塞状态

          冻结状态(休眠/无限等待)

    新建 :刚创建出来的线程,即 new Thread();

    阻塞 :没有抢到CPU,在等待CPU调用

    运行 :调用run 方法,在运行状态

    死亡 :方法结束,调用完成

    休眠 :调用sleep() 方法,进入休眠状态

sleep 是Thread 的一个函数

sleep 指占用CPU 不工作,其他线程无法进入。即:sleep不会让出系统资源;

    无限等待 : 调用wait() 方法,未被唤醒

wait 是object 的一个函数,需要调用notify() 来唤醒

wait 指不占用CPU 不工作,其他线程可以进入。即:wait是进入线程等待池中等待,让出系统资源。

java中的多线程 // 基础的更多相关文章

  1. Java中的多线程基础

    1.线程与进程 进程: 进程是程序运行以及资源分配的基本单位,一个程序至少有一个进程. 如下图所示: 线程: 线程是CPU调度和分配的基本单位,一个进程至少有一个线程. 同一个进程中的线程共享进程资源 ...

  2. Java中的多线程=你只要看这一篇就够了

    如果对什么是线程.什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内. 用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现.说这个话其 ...

  3. Java中的多线程技术全面详解

    本文主要从整体上介绍Java中的多线程技术,对于一些重要的基础概念会进行相对详细的介绍,若有叙述不清晰或是不正确的地方,希望大家指出,谢谢大家:) 为什么使用多线程 并发与并行 我们知道,在单核机器上 ...

  4. Java 中传统多线程

    目录 Java 中传统多线程 线程初识 线程的概念 实现线程 线程的生命周期 常用API 线程同步 多线程共享数据的问题 线程同步及实现机制 线程间通讯 线程间通讯模型 线程中通讯的实现 @(目录) ...

  5. 第87节:Java中的Bootstrap基础与SQL入门

    第87节:Java中的Bootstrap基础与SQL入门 前言复习 什么是JQ? : write less do more 写更少的代码,做更多的事 找出所有兄弟: $("div" ...

  6. Java中使用多线程、curl及代理IP模拟post提交和get访问

    Java中使用多线程.curl及代理IP模拟post提交和get访问 菜鸟,多线程好玩就写着玩,大神可以路过指教,小弟在这受教,谢谢! 更多分享请关注微信公众号:lvxing1788 ~~~~~~ 分 ...

  7. 【转】Java中的多线程学习大总结

    多线程作为Java中很重要的一个知识点,在此还是有必要总结一下的. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程 ...

  8. Java中的 多线程编程

    Java 中的多线程编程 一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序 ...

  9. Android学习记录(5)—在java中学习多线程下载之断点续传②

    在上一节中我们学习了在java中学习多线程下载的基本原理和基本用法,我们并没有讲多线程的断点续传,那么这一节我们就接着上一节来讲断点续传,断点续传的重要性不言而喻,可以不用重复下载,也可以节省时间,实 ...

随机推荐

  1. NYOJ 129 树的判定 (并查集)

    题目链接 描述 A tree is a well-known data structure that is either empty (null, void, nothing) or is a set ...

  2. xp,win7,win10系统安装GHO镜像下载

    淘宝买的纯净版系统镜像 不含任何垃圾软件 极致纯净 链接:http://pan.baidu.com/s/1eR12db0 密码:opjm 链接:http://pan.baidu.com/s/1mhEN ...

  3. N-gram语言模型与马尔科夫假设关系(转)

    1.从独立性假设到联合概率链朴素贝叶斯中使用的独立性假设为 P(x1,x2,x3,...,xn)=P(x1)P(x2)P(x3)...P(xn) 去掉独立性假设,有下面这个恒等式,即联合概率链规则 P ...

  4. [Leetcode] Sum 系列

    Sum 系列题解 Two Sum题解 题目来源:https://leetcode.com/problems/two-sum/description/ Description Given an arra ...

  5. [转载]C++多态技术

    摘自: http://www.royaloo.com/articles/articles_2003/PolymorphismInCpp.htm http://blog.sciencenet.cn/bl ...

  6. MySQL-IN和Exists区别

    1.in和exists in是把外表和内表作hash连接,而exists是对外表作loop循环,每次loop循环再对内表进行查询.一直以来认为exists比in效率高的说法是不准确的.  exists ...

  7. Activity工作流 -- java运用

    一. 什么是工作流 以请假为例,现在大多数公司的请假流程是这样的 员工打电话(或网聊)向上级提出请假申请——上级口头同意——上级将请假记录下来——月底将请假记录上交公司——公司将请假录入电脑 采用工作 ...

  8. python 多线程, 多进程, 协程

    1. 介绍: threading用于提供线程相关的操作,线程是应用程序中工作的最小单元.python当前版本的多线程库没有实现优先级.线程组,线程也不能被停止.暂停.恢复.中断. 2. 1  线程执行 ...

  9. Hadoop案例(四)倒排索引(多job串联)与全局计数器

    一. 倒排索引(多job串联) 1. 需求分析 有大量的文本(文档.网页),需要建立搜索索引 xyg pingping xyg ss xyg ss a.txt xyg pingping xyg pin ...

  10. Yii 简明学习教程

    Yii是一个基于组件的高性能PHP框架,用于快速开发Web应用程序(下面内容基于Yii 1.1) 1. 典型的工作流 用户向入口脚本index.php发起请求 入口脚本加载应用配置config.php ...