十、多线程

1、基本概念

一个exe在内存中有一个单独的地址,程序一般对应一个进程
一个进程中如需同时运行多个子程序时,需要开启多个线程
当一个Java程序启动时,JVM会自动创建主线程,并在该线程中调用程序的main()方法
JVM还创建了其他线程,例如,与垃圾收集、对象终止和其它JVM内务处理任务相关的线程。
合理的线程使用可以帮助我们显著的提供程序运行效率

2、线程的实现

Java语言中实现线程的方式有两种,但都是通过start()方法启动线程实例

  • 第一种,继承线程类:Thread
继承Thread类:class Test extends Thread{public void run(){while(true){System.out.println("o");try {sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}      }}
}
实例化:
Test td = new Test();
启动线程
td.start();
  • 第二种:实现Runnable接口
实现runable接口:class Test implements Runnable{public void run(){while(true){System.out.println("o");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}        }}}实例化并建立线程对象:Thread td = new Thread(new Test());运行线程:td.start();

Daemon线程

先来看个多线程的例子

public class ParentTest  {  public static void main(String[] args)  {  System.out.println("parent thread begin ");  ChildThread t1 = new ChildThread("thread1");  ChildThread t2 = new ChildThread("thread2");  //t1.setDaemon(true);  //t2.setDaemon(true);  t1.start();  t2.start();  System.out.println("parent thread over ");  }  }  class ChildThread extends Thread  {  private String name = null;  public ChildThread(String name)  {  this.name = name;  }  @Override  public void run()  {  System.out.println(this.name + "--child thead begin");  try  {  Thread.sleep(500);  }  catch (InterruptedException e)  {  System.out.println(e);  }  System.out.println(this.name + "--child thead over");  }  }  执行结果如下:  parent thread begin  parent thread over  thread1--child thead begin  thread2--child thead begin  

程序在主程序运行结束后,子程序依然进行;
若在主线程中创建了子线程,如果希望在主线程结束时,子线程也结束,这时候就需要Daemon线程

一个Daemon线程是一个在后台执行服务的子线程。 如果所有的非Daemon的线程都结束了,则Daemon 线程就会自动终止。
例如:main方法是一个非Daemon线程,如果希望main方法结束后子线程跟着终止,要将它设为Daemon线程。
Daemon线程设置方法: Thread.setDaemon(true);

  • 第三种:实现Callable接口
    比较推荐实现Runnable接口的方式,原因如下:
(1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数
据有效的分离,较好地体现了面向对象的设计思想。 (可联想到模拟火车站卖票的例子) (2)可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经
继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread
类的方式,那么,这个类就只能采用实现Runnable接口的方式了。 (单继承多实现) (3)有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代
码来自同一个类的实例时,即称它们共享相同的代码。多个线程操作相同的数据,与它们的代码
无关。当共享访问相同的对象时,即它们共享相同的数据。当线程被构造时,需要的代码和数据
通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。

4、线程的生命周期

  • 生命的几个基本状态

  • 运行中的方式

 1、执行了start()之后,线程进入了Runnable状态,此时线程尚未真正开始执行2、必须等待排班器(Scheduler)的排班,决定运行的先后顺序


有几种状况会让线程进入Blocked状态

等待输入输出;
调用sleep()方法;
尝试取得对象锁定;
调用wait()方法;

Blocked线程如何恢复到Runnable状态?

输入输出完成;
调用interrupt();
取得对象锁定;
调用notify()或notifyAll();

5、线程的休眠与唤醒

Thread thread = new Thread(new Runnable() {public void run() {try {Thread.sleep(99999);}catch(InterruptedException e) {System.out.println("I'm interrupted!!");}System.out.println("Hello Thread");}
});
thread.start();
thread.interrupt();

6、线程的加入

  • 当线程使用join()加入至另一个线程时,另一个线程会等待这个被加入的线程工作完毕,然后再继续它的动作。
  • join()的意思表示将线程加入成为另一个线程的流程之一
Thread threadB = new Thread(new Runnable() {public void run() {…    }
});
threadB.start();
try {threadB.join(); //子线程threadB加入主线程main中
}catch(InterruptedException e) {e.printStackTrace();
}

7、线程的停止

  • 不建议使用stop()来停止一个线程的运行,而是采用向run方法传递一个Boolean型的信号量,以控制其结束与开始
public class SomeThread implements Runnable {private boolean flag = true;public void stopThread() {this.flag = false;}public void run() {while(isContinue) {// ... some statements}}
}

8、 线程的同步化 (关键字synchronized)

同步化的作用
  • 一个进程中的多个线程共享相同的内存地址空间,这就意味着它们可以访问相同的变量和对象,而且它们从同一堆中分配对象。
  • 如果没有同步,数据很容易就处于不一致状态。例如,如果一个线程正在更新两个相关值,而另一个线程正在读取这两个值,有可能在第一个线程只写了一个值,还没有写另一个值的时候,调度第二个线程运行,这样它就会看到一个旧值和一个新值。
  • 如果一个对象所持有的数据可以被多线程同时共享存取时,必须考虑到「数据同步」的问题。
对象锁(lock)

实现方式
  • 使用“synchronized”关键词为方法加同步锁
public class ThreadTest implements Runnable { public synchronized void run() {for (int i = 0; i < 10; i++) {System.out.print(" " + i);}}public static void main(String[] args) {Runnable r = new ThreadTest();Thread t1 = new Thread(r);Thread t2 = new Thread(r);t1.start();t2.start();}
} 
  • 使用“synchronized”关键词为语句块加同步锁

public class ThreadTest implements Runnable {public void run() { synchronized(this){ for (int i = 0; i < 10; i++) {System.out.print(" " + i);}}}public static void main(String[] args) {Runnable r = new ThreadTest();new Thread(r).start();new Thread(r).start();}
}

9、线程的等待与恢复

  • wait()、notify()与notifyAll()是由Object类别所提供的声明为“final”的方法。
  • 在同步化的方法或区块中呼叫wait()方法,当前的线程会被放入对象的等待池中,使线程暂停。
  • 当notify()方法被调用,JVM会通知等待池中的线程加入,回到锁定池的Blocked状态。
  • 被通知的线程是随机的,被通知的线程会与其它线程共同竞争对象的锁定。
  • 如果您呼叫notifyAll(),则所有在等待池中的线程都会被通知回到锁定池的Blocked状态。
    例子:
package com.phy.thread;import java.util.Scanner;/*** @author :xp* @date :Created in 2018/10/26 11:00* @description:*/
class MT implements Runnable{public boolean flog = false;public  void run() {int i = 0 ;while(true){System.out.println("Hello World"+i);i++;if(flog){synchronized(this){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
public class TestDaemon {public static void main(String[] args){MT mt = new MT();Thread t = new Thread(mt);t.start();Scanner sc = new Scanner(System.in);while(true){System.out.println("输入0暂停");int n = sc.nextInt();if(n==0) mt.flog = true;System.out.println("输入1继续");n = sc.nextInt();if(n==1){synchronized (mt) {mt.flog = false;mt.notify();}}}}}

10、线程的优先级

  • 线程有其优先权,由1(Thread.MIN_PRIORITY)到10(Thread.MAX_PRIORITY)
  • 优先权越高,排班器越优先排入执行,如果优先权相同,则轮流执行(Round-robin方式)
  • 可以通过线程的setPriority()方法设置某个线程的优先级,这样,当一个资源空闲时,同时等待这个资源的两个线程里边优先级高的会先获得对象执行权

11、容器类的线程安全

  • Collections提供了一系列synchronized方法来传回一个同步化的容器对象,从而保证了容器类的线程安全。
  • 使用Iterator遍访对象时,必须实现同步化。
synchronized(list) {Iterator i = list.iterator();     while (i.hasNext()) {System.out.println(i.next());}
}

线程案例:写出输出顺序为男,女,男,女的线程

  • 方式一

//首先要定义一个User对象这里省略package com.phy.thread;import com.phy.entity.User;/*** @author xp* @date :Created in 2018/10/26 14:52* @description:设计一个线程类,第一次输出小明男提问* 第二次输出小红女在提问*///定义一个线程类
class RunnableExample implements Runnable {private boolean flag = true;User userB = new User("小明","男",1);User userG = new User("小红","女",1);public RunnableExample() {}@Overridepublic synchronized void run() {while (true){if(flag) {System.out.println(userB.getName() + userB.getSex() + "正在问问题");}else {System.out.println(userG.getName() + userG.getSex() + "正在问问题");}flag = !flag;try {Thread.currentThread().sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}
//测试函数
public class Test2 {public static void main(String args[]){Thread threadG = new Thread(new RunnableExample());threadG.start();}
}
  • 方式二

//首先要定义一个User对象这里省略package com.phy.thread;import com.phy.entity.User;/*** @author :xp* @date :Created in 2019/3/26 14:52* @description:设计一个线程类,输出顺序为男,女,男,女* 第二次输出小红女在提问*///定义一个线程类
class RunnableExample implements Runnable {private User user;boolean flag ;User userB = new User("小明","男",1);User userG = new User("小红","女",1);public RunnableExample(User user,boolean flag) {this.user = user;this.flag =flag;}@Overridepublic  void run() {synchronized(user){while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if( flag ){System.out.println(userB.getName() + userB.getSex() + "正在问问题");}else {System.out.println(userG.getName() + userG.getSex() + "正在问问题");}user.notifyAll();try {user.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}
}
//测试函数
public class Test2 {public static void main(String args[]){User c= new User();Thread threadB = new Thread(new RunnableExample(c,true));Thread threadG = new Thread(new RunnableExample(c,false));threadB.start();threadG.start();}
}

写两个线程,一个线程打印 1~52,另一个线程打印字母A-Z。打印顺序为12A34B56C……5152Z

  • 这是线程间的通信的一个很有代表性的例子。过程如下:
    1、创建两个线程实现Runnable接口重写run方法,一个用于打印数字,一个用于打印字母。
    2、创建一个测试类,在测试类中创建一个Object类的对象(作为两个线程的共享资源,以便实现线程间的通信),通过各类的构造方法传递过去。
    3、在两个类的run方法中都要用synchronized保证同步,即加锁。
    4、在数字类中用for循环每打印两个数字就唤醒其他线程,释放锁,进入阻塞状态。 在字母类中每打印一个字母就唤醒其他线程,释放锁,进入阻塞状态。
  • 在写这个程序的时候有几点要注意的地方:
    1、两个线程要使用同一个资源才需相互通信,所以在测试类中创建共享资源,并通过构造方法分别传到各线程类中。
    2、两个线程哪个先运行(执行start())哪个就先获得资源并执行
    3、在run方法体内写进程间的通信wait()和notifyall()时,一定要先写notifyall()再写wait()。
    原因:当你先写wait()时,本进程也进入休眠状态,再写notifyall()唤醒所有线程时本线程以及其他线程被一块唤醒,竞争同一个资源,就会造成死锁。 所以一定要先唤醒其他线程,再让本线程阻塞!
/*** @author :xp* @date :Created in 2018/10/26 16:12* @description:实现两个线程之间的通信*/
public class ThreadExample {public static void main(String[] args) {Object obj = new Object();Number s = new Number(obj);Char z = new Char(obj);Thread th1 = new Thread(s);Thread th2 = new Thread(z);th1.start();//数字的线程先运行,数字先执行th2.start();}}//数字类实现runnable接口
class Number implements Runnable{private Object obj;public Number() {}public Number(Object obj) {this.obj = obj;}@Overridepublic void run() {synchronized(obj){//给共享资源上锁for(int i = 1;i < 53;i++ ){System.out.println(i);if(i % 2 == 0){//保证输出两个数字obj.notifyAll();//唤醒其他线程try {obj.wait();//等待并释放锁} catch (InterruptedException e) {e.printStackTrace();}}}}}
}
//字母类
class Char implements Runnable{private Object obj;public Char() {}public Char(Object obj) {this.obj = obj;}@Overridepublic void run() {synchronized(obj){for(int i = 0;i < 26;i++ ){System.out.println((char)(i+'A'));obj.notifyAll();//唤醒其他线程try {obj.wait();//释放锁等待} catch (InterruptedException e) {e.printStackTrace();}}}}
}

java 多线程基础相关推荐

  1. Java多线程干货系列(1):Java多线程基础

    转载自  Java多线程干货系列(1):Java多线程基础 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学 ...

  2. JAVA多线程基础篇-关键字synchronized

    1.概述 syncronized是JAVA多线程开发中一个重要的知识点,涉及到多线程开发,多多少少都使用过.那么syncronized底层是如何实现的?为什么加了它就能实现资源串行访问?本文将基于上述 ...

  3. 爬梯:Java多线程基础

    学习资源:狂神说 Java多线程基础 1.多线程概述 Process 进程 一个进程可以有多个线程. Thread 线程 线程就是独立的执行路径 在程序运行时,即使没有自己创建线程,后台也会有多个线程 ...

  4. java多线程基础学习[狂神说java-多线程笔记]

    java多线程基础学习 一.线程简介 1.类比 2.程序进程线程 3.线程的核心概念 二.线程的实现(重点) 调用方法与调用多线程的区别 Thread 类 1.thread使用方法 2. 代码实现 3 ...

  5. Java多线程干货系列—(一)Java多线程基础

    前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学习之旅吧. 正文 线程与进程 1 线程:进程中负责程序执行的 ...

  6. 一篇文章弄懂Java多线程基础和Java内存模型

    文章目录 一.多线程的生命周期及五种基本状态 二.Java多线程的创建及启动 1.继承Thread类,重写该类的run()方法 2.通过实现Runnable接口创建线程类 3.通过Callable和F ...

  7. java多线程基础视频_【No996】2020年最新 Java多线程编程核心基础视频课程

    01.课程介绍.mp4 02.多线程编程基础-进程与线程.mp4 03.多线程编程基础-使用多线程-继承Thread类.mp4 04.多线程编程基础-使用多线程-实现Runnable接口.mp4 05 ...

  8. Java多线程基础(一)

    文章分两份,第一份主要内容为1-4点 1.多线程的概念 2.Java程序运行原理 3.实现多线程的2种方式 4.2种方式的区别 5.线程同步(同步锁) 6.同步线程之间的通讯 1.多线程的概念 (1) ...

  9. Java多线程基础知识

    多线程基础知识 这是我再次学习多线程知识的一个总结,对于刚刚接触的学习者是比较友好易懂的,便于快速的理解和掌握. 一.基本概念: 1.进程:进程就是运行中的程序,当一个程序开始执行,操作系统就会给这个 ...

  10. 【Java进阶营】JAVA多线程基础学习二:synchronized

    本篇主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题,没错就是使用synchronized. 一.如何解决线程安全问题? 一般 ...

最新文章

  1. web11 Struts处理表单数据
  2. linux连接wifi账户密码忘了怎么办,wifi登录密码忘了怎么办?
  3. 修改参数failed_login_attempts=unlimited
  4. insert时调用本身字段_MySQL RC级别下并发insert锁超时问题 - 案例验证
  5. Tableau上面地图与条形图结合_何为计算的详细级别,认识Tableau中的Level of Detail...
  6. 20151227感知机(perceptron)
  7. Google发布Puppeteer 1.0
  8. 显示js对象所有属性和方法的函数
  9. The Closest M Points//kd树+优先队列
  10. springboot和springframework以及jdk版本的对应关系
  11. CF1151div2(Round 553)
  12. paip.图片搜索工具总结
  13. Win7环境下如何开启802.1X 无线网络认证
  14. H.264开源解码器评测
  15. 夜曲编程python_夜曲编程免费PC版-夜曲编程电脑版下载 v1.0.0--PC6电脑版
  16. 黄家驹的《光辉岁月》永远流传,但IT人的光辉岁月已渐远去
  17. 第一篇 香橙派刷机和开发环境准备(Armbian版)
  18. 在职非全日制计算机考研上岸心得分享
  19. 为什么选择Chrome浏览器
  20. 计算机网络基础常见名词缩写汇总

热门文章

  1. 如何在众多百世快递物流中快速筛选出更新量为1的单号
  2. 自然语言处理(NLP):概述【NLP技术的四个维度:声音、单词、句子结构、语义】
  3. Mac内录音频(微信语音导出的一种方式)
  4. 漫画:什么是树状数组?
  5. jain-sip 开发文档
  6. HTML基础————table标签
  7. uni-app APP开发升级功能
  8. html锚点链接dw怎么做,Dreamweaver网页中如何制作锚点链接
  9. scp、rsync与集群分发
  10. 小红书-竞品分析报告