多线程

1.什么是多线程?

多线程:某一个程序在运行的时候【进程】可能会产生多个不同的执行线索【执行轨迹】【线程】,这些多个不同的执行线索【执行轨迹】共同运行的情况就是多线程。往往我们会感觉到这些多个不同的执行线索【执行轨迹】同时执行,实际上这时一种错觉假象,实际上当这些多个不同的执行线索【执行轨迹】在运行的时候,某一个时刻只用一个执行线索【执行轨迹】在运行,只是这多个不同的执行线索【执行轨迹】快速的切换而已。
“暴风影音”播放电影的时候,我们感觉图像和声音在同时运行,实际上你被骗了,因为程序在执行的时候,某一个时刻只能有一条线程运行,那么为啥你感觉图像和声音在同时运行,因为进程控制线程快速的切换导致,我们感觉图像和声音在同时运行。

2.多线程的创建方式以及区别?

Thread类
java.lang 类 Thread【程序中的执行线程】【不需要导包】
public class Thread extends Object implements Runnable
如果我们要想在JAVA中使用线程,就必须通过Thread类完成。
如何通过Thread类创建一个线程
新建一个java类,继承Thread类。
重写run方法
将需要由线程执行动作,写入run方法

public class MyThread extends Thread{@Overridepublic void run() {for(int i=1;i<=100;i++){System.out.println("MyThread=="+i);}}
}

如何运行通过Thread类创建的线程?
1.创建线程对象
2.通过线程对象调用继承自Thread类的start方法启动线程运行。

public class ThreadTest {public static void main(String[] args) {//1.创建线程对象MyThread  th1=new MyThread();MyThread  th2=new MyThread();//直接调用run方法执行不是启动线程运行的方式。//直接调用run方法执行实际上是对象调用方法运行的过程。//th1.run();//th2.run();//2.通过线程对象调用继承自Thread类的start方法启动线程运行。//启动线程运行是需要使用Thread类的start方法。//Thread类的start方法会触发run方法运行。th1.start();th2.start();}
}

Runnable接口
java.lang 接口 Runnable
public interface Runnable
Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。

Thread类与Runnabl接口的关系
Thread类实现过Runnabl接口,Thread类是Runnabl接口的子类。
理解Thread类中的run方法,实际上是来自Runnabl接口
如何通过Runnable 接口创建一个线程?
新建java类,实现Runnable 接口。
重写run方法
将需要由线程执行动作,写入run方法

public class TestRun implements Runnable{@Overridepublic void run() {for(int i=1;i<=100;i++){System.out.println("TestRun=="+i);}}
}

3.线程的常用方法?

Thread类的常用方法:

线程名名称,java程序运行以后会给程序中的每一个线程一个名称,默认的主线程的名称是”main”.,非主线程的其他线程名称的默认名称都是“Thread-0”“Thread-1”,“Thread-2”,以此类推。

public class MyThread implements Runnable {@Overridepublic void run() {for(int  i=1;i<=100;i++){//得到当前正在运行的线程名称String name=Thread.currentThread().getName();System.out.println(name+"  i="+i);}}
}MyThread  my=new MyThread();
Thread  th1=new Thread(my);  //Thread-0
Thread  th2=new Thread(my);  //Thread-1
th1.setName("线程1");
th2.setName("线程2");
th1.start();
th2.start();

得到/设置线程的优先级

线程的优先级:就是线程的执行先后。
因为java中的线程的调用执行是jvm中的线程调度器负责管理线程,在java中线程的执行先后,我们是控制不了的。
我们无法控制线程的执行次序,但是我们可以通过设置线程的优先级,控制线程优先执行的几率。
优先级数字越大优先级越高,被优先执行的几率增大,并不是说谁的优先级大谁就一定先执行。
我们将线程的优先级分为10个级别【1–10】,数字越大优先级越高。

Java还为我们提供了3个静态常量,用于保存

默认java为每一个线程设置的优先级都是一样的级别为5【NORM_PRIORITY】
我们可以通过setPriority来修改线程的优先级,数字越大优先级越高,被优先执行的几率增大,并不是说谁的优先级谁就一定先执行。
注意:绝对不能用修改线程的优先级,来确保某一个线程就要先执行。

MyThread  my=new MyThread();Thread  th1=new Thread(my);  //Thread-0Thread  th2=new Thread(my);  //Thread-1th1.setName("线程1");th2.setName("线程2");th1.setPriority(1);th2.setPriority(10);int pri1=th1.getPriority();int pri2=th2.getPriority();System.out.println("第一个线程的优先级是=="+pri1);System.out.println("第二个线程的优先级是=="+pri2);th1.start();th2.start();

守护线程:

守护线程:我们也叫精灵线程,普通的线程又叫用户线程。
当所有用户线程都执行完毕以后,无论守护线程能否继续运行,都要立刻停止运行。【共死】

public class MyThread implements Runnable {@Overridepublic void run() {for(int  i=1;i<=100;i++){//得到当前正在运行的线程名称String name=Thread.currentThread().getName();System.out.println(name+"  i="+i);}}
}public class MyThread2 implements Runnable {@Overridepublic void run() {while(true){System.out.println("守护线程!!!!");}}}public class ThreadTest {public static void main(String[] args) {MyThread  my=new MyThread();Thread  th1=new Thread(my);Thread  th2=new Thread(my);MyThread2  my2=new MyThread2();Thread  th3=new Thread(my2);//void setDaemon(boolean on) 将该线程标记为守护线程用户线程。th3.setDaemon(true);// boolean    isDaemon() 测试该线程是否为守护线程。//System.out.println("th1--守护线程?--"+th1.isDaemon());//System.out.println("th2--守护线程?--"+th2.isDaemon());//System.out.println("th3--守护线程?--"+th3.isDaemon());th1.start();th2.start();th3.start();}
}

4.线程的生命周期

Java中线程的生命周期
1、线程的生命周期就是线程从一开始创建,到run方法执行完毕以后的状态变化。[状态之间的切换]
2、线程的生命周期几种状态【1、新建状态 2、就绪状态 3、运行状态 4.阻塞状态 5.死亡状态】

创建状态:通过new的方式创建出线程对象,此时线程就进入到创建状态【新建状态】。
新建状态的线程是不能运行。
就绪状态:新建状态的线程调用strat方法之后就会进入就绪状态。
就绪状态的线程具备执行能力,但是没有cpu资源。【万事具备只差cpu】.
运行状态:就绪状态的线程得到cpu资源开始执行run方法,此时这个线程就是运行状态。
运行状态的线程当cpu切换到其他线程时候,本线程就再一次进入就绪状态。
阻塞状态:运行状态的线程调用sleep/wait方法…此时线程进入阻塞状态。
处于阻塞状态的线程的休眠时间到/调用notify方法/notifyAll方法在此时线程进入就绪状态,从就绪状态中得到cpu资源从而进入运行状态。
死亡状态:运行状态的线程run方法运行结束/线程执行过程中遇到异常/调用stop方法此时线程就进入到死亡状态。
死亡状态的线程是不能运行,除非再一次使用strat方法重新启动运行。

5.为什么需要线程同步/线程安全?什么是线程同步/线程安全?线程同步/线程安全实现方式有几种,它们有什么区别?

原因是因为CPU在执行某个线程的时候,并没有将线程的任务全部执行完成就切换到其他的线程上导致数据有误。
原因:多条线程去访问同一个资源的时候,可能会出现资源访问数据不一致的情况,为了避免这种情况的出现,就需要使用线程安全【线程同步】。
解决安全问题:线程的同步技术。
同步的目的就是保证有一个线程在执行任务代码的时候,其他线程要在外面等待。
同步原理:只要某些代码(牙医)被添加了同步(门,应该门上的那个锁),那么任何线程在进入被同步控制的代码的时候都需要判断有没有其他某个线程在同步中(要想看牙医,需要先能够打开锁),如果有当前其他的任何线程都需要在同步的外面等待,如果没有这时只有某一个线程可以进入到同步中,其他线程就继续在同步的外面等待。
【线程安全/线程同步】:多条线程去访问同一个资源的时候,每一次保证一条线程正常访问资源,当前该线程访问资源的时候,其他的线程就需要等待,当前该线程访问资源结束之后,允许另一条线程去访问资源,其他线程继续等待。

1.同步代码块
同步代码块的书写格式:
synchronized(任意的对象[锁] ){
书写的被同步的代码(操作共享数据的代码);
}

/*** 实现买票程序的线程类* @author Administrator**/
public class MyThread implements Runnable{//定义一个变量,来保存票数//假设我们现在有5张票可以卖出private int piao=5;@Overridepublic void run() {//1.通过while循环控制买票的持续性//定义一个变量,来控制while循环boolean flag=true;while(flag){/*** 同步代码块格式* synchronized(任意的对象[锁] ){书写的被同步的代码(操作共享数据的代码);}*synchronized---同步关键字*(同步对象)--需要被锁定的资源所在类的对象*{}---【块】*将需要同步的Java程序写上面的{}块中*/synchronized(this){//2.判断是否有票//如果票数大于0,就表是有票,可以卖出if(piao>0){//3.线程休眠模拟出收钱,打票,找钱的过程try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//得到当钱包线程的名称String name=Thread.currentThread().getName();//4.卖出第几张票//5.票数减1System.out.println(name+",卖出第1张票,还剩"+(--piao)+"张票");}else{//如果票数小于/等于0,就是已经无票可卖flag=false;}}}}
}

2.同步方法
同步方法格式:
访问限制修饰符 synchronized 返回值类型 方法名称(参数列表){
书写的被同步的代码(操作共享数据的代码);
}
被synchronized 关键字修饰的方法就是同步方法

/*** 实现买票程序的线程类* @author Administrator**/
public class MyThread implements Runnable{//定义一个变量,来保存票数//假设我们现在有5张票可以卖出private int piao=5;//定义一个变量,来控制while循环private boolean flag=true;@Overridepublic void run() {//1.通过while循环控制买票的持续性while(flag){//调用同步方法的执行seller();  }}/*** 创建买票的同步方法* 同步方法格式:访问限制修饰符  synchronized  返回值类型  方法名称(参数列表){书写的被同步的代码(操作共享数据的代码);}*/private  synchronized void seller(){//2.判断是否有票//如果票数大于0,就表是有票,可以卖出if(piao>0){//3.线程休眠模拟出收钱,打票,找钱的过程try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//得到当钱包线程的名称String name=Thread.currentThread().getName();//4.卖出第几张票//5.票数减1System.out.println(name+",卖出第1张票,还剩"+(--piao)+"张票");}else{//如果票数小于/等于0,就是已经无票可卖flag=false;}}
}

3.使用JDK5中的Lock接口取代同步代码块
JDK5中的Lock接口
在JDK5版本之前:解决多线程的同步问题使用同步代码块。在JDK5之后,提供另外一个接口Lock。它可以代替同步代码块。
java.util.concurrent.locks 接口 Lock
public interface Lock
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
Lock接口,它实现了比同步代码块更加方便的同步操作。Lock接口中提供由程序员自己手动调用方法来获取同步锁和释放同步锁。
同步代码块获取锁和释放锁都是隐式看不见的。
void lock()获取锁。
void unlock()释放锁。
使用java.util.concurrent.locks.ReentrantLock类创建Lock接口的对象。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** 实现买票程序的线程类* @author Administrator**/
public class MyThread implements Runnable{//定义一个变量,来保存票数//假设我们现在有5张票可以卖出private int piao=5;//定义一个变量,来控制while循环private boolean flag=true;//创建一个Lock接口对象[接口回调对象]private Lock lock=new ReentrantLock();@Overridepublic void run() {//1.通过while循环控制买票的持续性while(flag){//锁定资源lock.lock();//2.判断是否有票//如果票数大于0,就表是有票,可以卖出if(piao>0){//3.线程休眠模拟出收钱,打票,找钱的过程try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//得到当钱包线程的名称String name=Thread.currentThread().getName();//4.卖出第几张票//5.票数减1System.out.println(name+",卖出第1张票,还剩"+(--piao)+"张票");}else{//如果票数小于/等于0,就是已经无票可卖flag=false;}//释放资源锁定lock.unlock();}}
}

6.sleep 与wait的区别

对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。

sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。

在调用sleep()方法的过程中,线程不会释放对象锁。

而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

从使用角度看,sleep是Thread线程类的方法,而wait是Object顶级类的方法。

sleep可以在任何地方使用,而wait只能在同步方法或者同步块中使用。

CPU及资源锁释放

sleep,wait调用后都会暂停当前线程并让出cpu的执行时间,但不同的是sleep不会释放当前持有的对象的锁资源,到时间后会继续执行,而wait会放弃所有锁并需要notify/notifyAll后重新获取到对象锁资源后才能继续执行。

sleep和wait的区别:
1、sleep是Thread的静态方法,wait是Object的方法,任何对象实例都能调用。
2、sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)。
3、它们都可以被interrupted方法中断。
Thread.Sleep(1000) 意思是在未来的1000毫秒内本线程不参与CPU竞争,1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。另外值得一提的是Thread.Sleep(0)的作用,就是触发操作系统立刻重新进行一次CPU竞争,竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。

wait(1000)表示将锁释放1000毫秒,到时间后如果锁没有被其他线程占用,则再次得到锁,然后wait方法结束,执行后面的代码,如果锁被其他线程占用,则等待其他线程释放锁。注意,设置了超时时间的wait方法一旦过了超时时间,并不需要其他线程执行notify也能自动解除阻塞,但是如果没设置超时时间的wait方法必须等待其他线程执行notify。

                  wait sleep
同步 只能在同步上下文中调用wait方法,否则或抛出IllegalMonitorStateException异常 不需要在同步方法或同步块中调用
作用对象 wait方法定义在Object类中,作用于对象本身 sleep方法定义在java.lang.Thread中,作用于当前线程
释放锁资源
唤醒条件 其他线程调用对象的notify()或者notifyAll()方法 超时或者调用interrupt()方法体
方法属性 wait是实例方法 sleep是静态方法

7.notify 与notifyAll的区别

notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。
两者的最大区别在于:
notifyAll使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
notify则文明得多他只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。

JAVA基础要点复习(10)—个人笔记相关推荐

  1. 传智播客 刘意_2015年Java基础视频-深入浅出精华版 笔记(2015年10月25日23:28:50)

    day01 win 7系统打开DOS有趣方法:按住shift+右键,单击"在此处打开命令窗口"(注意:在此处可以是任何的文件夹,不一定是桌面) 用DOS删除的文件不可以在回收站恢复 ...

  2. Java基础知识复习(一)

    Java基础知识复习(一) 目录 Java简介 命名规则 八种基本的数据类型 字面量 类型转换 变量的形态 逻辑运算符 位运算 移位运算 习题知识点 目录 Java简介 Java是由Sun公司在199 ...

  3. java基础知识复习(上半)

    java基础知识复习 java为什么被发明? Green项目的确立,应用于像电视盒一样的消费类电子产品,语言本身本身中立. java的三大版本? javaSE的定位在于客户端,只要用于桌面应用软件的编 ...

  4. java基础练习复习二:递归字节流字符流二

    本篇是基于java基础练习复习一:递归&字节流&字符流一, 如果对您有帮助 ,请多多支持.多少都是您的心意与支持,一分也是爱,再次感谢!!!打开支付宝首页搜"55672346 ...

  5. 第一阶段:Java基础总复习一一一和一一一面向对象OOP总复习

    一.Java基础 0.Maven的介绍: 官网:https://maven.apache.org/download.cgi (1)Maven是什么? Maven 是 Apache 开源组织奉献的一个开 ...

  6. Java基础知识 廖雪峰教程笔记

    Java基础知识 Java基础知识 java简介 名词解释 运行Java程序 Java基本数据类型 模块 包装类型 记录类 异常处理 Java异常 使用Commons Logging 使用log4j ...

  7. 第一篇Java基础知识复习

    全章节学习目标: 1.掌握Java基本语法.常量.变量.流程控制语句.方法以及数组 2.了解Java代码的基本格式 3.熟悉Java运算符的使用 Java代码的基本格式 学习笔记(p14): 编写Ja ...

  8. 博学谷:Java基础知识精讲学习笔记——DAY 1

    1.IDEA项目目录详情 2. IDEA基本配置 ​ ①设置字体:File->Settings->Editor->Font ​ ②设置配色方案:File->Settings-& ...

  9. Java基础知识复习(二)

    对最近java基础知识理解不足的方面进行了巩固,写个博客记录一下!

最新文章

  1. Spark MLlib之使用Breeze操作矩阵向量
  2. XP,2003下使用route命令增加永久路由(静态路由)
  3. python 学习之 PythonAdvance2
  4. STM32CubeMX HAL库串口+DMA数据发送不定长度数据接收
  5. 信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1052:计算邮资
  6. CSDN 十大技术主题盘点-云原生篇
  7. pulsar的bookie服务变更journal或者ledger数据盘操作导向
  8. 王思聪5亿投资神话破灭?旗下普思资本股权遭冻结,冻结期3年
  9. Grafana实现zabbix数据可视化展示
  10. 智能仓储物流技术知识点汇总(部分)
  11. python nlpir_中文分词工具--NLPIR/ICTCLAS的Python版本使用
  12. Verilog基础知识4(常用集成门电路的逻辑符号对照表)
  13. vue富文本使用详解
  14. 竞价被恶意点击怎么办?该怎么屏蔽?
  15. 几款强力压缩打包软件
  16. 我男朋友是产品经理......
  17. 初学者如何从零开始搭建一个阿里云数据库
  18. 管理信息系统复试——三、管理信息系统战略规划与开发方法
  19. 程序员又要背锅?虾米音乐客户端代码惊现神注释:穷逼 VIP!
  20. 能够正常加入域但无法实施域策略

热门文章

  1. java版b2b2c多商家入驻微信小程序商城源码Spring Cloud+Spring Boot+mybatis+security+uniapp+直播带货+VR全景+前后端分离微服务商城源码
  2. Alamofire-Request
  3. android模拟器steam,退休模拟器steam版
  4. java 泰勒级数_java编程用泰勒级数计算arcsin
  5. 如何将ASP.NET MVC2项目升级到MVC 3 RC
  6. 今日分享:这4款音频降噪去杂音的软件,太好用了
  7. MRPII/ERP软件系统的实现(转)
  8. 哪个版本Rhinoceros支持M1intel Monterey系统?Rhinoceros for mac(犀牛建模)v7.3.21053.23032中文激活方法 功能介绍
  9. dwa的区别 teb_dwa杨算法解析1
  10. Medium 内部使用 css/less 的代码风格指南