线程是程序执行的一条路径, 一个进程中可以包含多条线程。多线程并发执行可以提高程序的效率,可以同时完成多项工作,多线程并发执行的实质就是CPU在做着高速的切换。多线程的应用场景:红蜘蛛同时共享屏幕给多个电脑;迅雷开启多条线程一起下载;QQ同时和多个人一起视频;服务器同时处理多个客户端请求。

并行和并发的区别

  • 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
  • 并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
  • 比如我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。

Java程序运行原理

Java命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

代码验证:

public class Demo1_Thread {  //此程序的运行结果是 "我是主线程的执行代码"和"垃圾被清扫了"交替执行/**证明jvm是多线程的*/public static void main(String[] args) {for(int i = 0; i < 100000; i++) {new Demo();}for(int i = 0; i < 10000; i++) {System.out.println("我是主线程的执行代码");}}
}class Demo {@Overridepublic void finalize() {System.out.println("垃圾被清扫了");}}

多线程的实现

方式一:定义类继承Thread,重写run方法,把新线程要做的事写在run方法中;创建线程对象,开启(调用start())新线程, 内部会自动执行run方法。

代码演示:

public class Demo2_Thread {public static void main(String[] args) {MyThread mt = new MyThread();        //4,创建Thread类的子类对象mt.start();               //5,开启线程MyThread mt1 = new MyThread();mt1.start();for(int i = 0; i < 1000; i++) {System.out.println("bb");}}}
class MyThread extends Thread {     //1,继承Threadpublic void run() {             //2,重写run方法for(int i = 0; i < 1000; i++) {        //3,将要执行的代码写在run方法中System.out.println("aaaaaaaaaaaa");}}
}

方式二:定义类实现Runnable接口,实现run方法,把新线程要做的事写在run方法中,创建自定义的Runnable的子类对象,创建Thread对象,传入Runnable,调用start()开启新线程,内部会自动调用Runnable的run()方法。

代码演示:

public class Demo3_Thread {public static void main(String[] args) {MyRunnable mr = new MyRunnable();    //4,创建Runnable的子类对象Thread t = new Thread(mr);      //5,将其当作参数传递给Thread的构造函数t.start();              //6,开启线程for(int i = 0; i < 1000; i++) {System.out.println("bb");}}}
class MyRunnable implements Runnable {          //1,定义一个类实现Runnable@Overridepublic void run() {                //2,重写run方法for(int i = 0; i < 1000; i++) {        //3,将要执行的代码写在run方法中System.out.println("aaaaaaaaaaaa");}}}

剖析实现Runnable接口的源码

源码解释:构造函数中传入了Runnable的引用,成员变量记住了它,start()调用run()方法时内部判断成员变量Runnable的引用是否为空,不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法。

源码解析:

public class Thread implements Runnable {    //Thread类本身实现了Runnable接口/*省略代码*/private Runnable target;    //一个Runnable类型的成员变量public Thread(Runnable target) {//Thread的有参构造方法,传入一个Runnable接口的子类对象target//调用init方法(下面这个) 把target传入init(null, target, "Thread-" + nextThreadNum(), 0);}private void init(ThreadGroup g, Runnable target, String name, long stackSize) {  //init方法里面又调用另一个重载的init方法(下面这个)把target传入init(g, target, name, stackSize, null);       }private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {/*省略代码*/this.target = target;//在init方法里面把成员变量target 进行赋值/*省略代码*/}public synchronized void start() { //当调用start方法的时候 内部调用的是start0()方法/*省略代码*/start0();/*省略代码*/}private native void start0(); //start0()是native修饰的方法 底层是其他语言 我们无法查看,但是我们知道底层会调用run方法@Overridepublic void run() {      //start0()的底层是会调用run()方法if (target != null) {  //判断成员变量target是否为nulltarget.run();  //如果target被赋值了,就要调用target的run方法}}/*省略代码*/
}

多线程两种实现方式的区别

继承Thread : 由于子类重写了Thread类的run(),当调用start()时, 直接找子类的run()方法;好处是:可以直接使用Thread类中的方法,代码简单;弊端是:如果已经有了父类,就不能用这种方法。

实现Runnable : 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法。好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的;弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂。

匿名内部类实现线程的两种方式

public static void main(String[] args) {new Thread() {                               //1,继承Thread类public void run() {                        //2,重写run方法for(int i = 0; i < 1000; i++) {        //3,将要执行的代码写在run方法中System.out.println("aaaaaaaaaaaaaa");}}}.start();                                  //4,开启线程new Thread(new Runnable() {                 //1,将Runnable的子类对象传递给Thread的构造方法public void run() {                     //2,重写run方法for(int i = 0; i < 1000; i++) {        //3,将要执行的代码写在run方法中System.out.println("bb");}}}).start();                                 //4,开启线程
}

public final String getName():获取线程对象的名称。默认情况下,名字的组成 Thread-编号(编号从0开始)
public final void setName(String name):设置线程名称。

代码演示:

//通过构造方法给name赋值
public static void demo1() {new Thread("芙蓉姐姐") {      //通过构造方法给name赋值public void run() {System.out.println(this.getName() + "....aaaaaaaaa");}}.start();
}
//通过setName()来设置线程的名字
public static void main(String[] args) {Thread t = new Thread() {public void run() {//this.setName("张三");        //在这儿设置也行  在外面设置名称也行System.out.println(this.getName() + "....aaaaaaaaaaaaa");}};t.setName("张三");   //通过setName()来设置线程的名字t.start();
}

public static Thread currentThread():返回当前正在执行的线程对象引用

public static void sleep(long millis):让当前线程休眠millis毫秒

public final void setDaemon(boolean on):设置线程为守护线程,一旦前台(主线程)结束,守护线程就结束了

public final void join():当前线程暂停,等待指定的线程执行结束后,当前线程再继续

public final void join(long millis):当前线程暂停, 等待指定的线程执行millis毫秒结束后, 当前线程再继续

public static void yield():暂停当前正在执行的线程对象,并执行其他线程。

public final int getPriority():获取线程优先级

public final void setPriority(int newPriority):更改线程的优先级。线程默认优先级是5。范围是1-10。

注意:优先级可以在一定的程度上,让线程获较多的执行机会。

代码演示:

public static void main(String[] args) {final Thread t1 = new Thread() {public void run() {for(int i = 0; i < 10; i++) {System.out.println(getName() + "...aaaaaaaaaaaaa");}}};Thread t2 = new Thread() {public void run() {for(int i = 0; i < 10; i++) {if(i == 2) {try {t1.join();  //t1插队后,t1执行结束后 t2才能接着执行//t1.join(1);    //插队指定的时间,过了指定时间后,两条线程交替执行} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(getName() + "...bb");}}};t1.start();t2.start();
}

同步代码块

当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作,这时就需要同步。如果两段代码是同步的,那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码。所谓同步代码块就是使用synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块,多个同步代码块如果使用相同的锁对象,那么他们就是同步的。

代码演示

public class Demo_Synchronized {public static void main(String[] args) {//匿名内部类使用局部变量,局部变量前面必须加final修饰,为了延长局部变量的声明周期final Printer p = new Printer();  new Thread() {public void run() {while(true) {p.print1();   //调用print1()}}}.start();new Thread() {public void run() {while(true) {p.print2();   //调用print2()}}}.start();}}class Printer {Demo d = new Demo();public void print1() {    synchronized(d) {   //同步代码块,锁机制,锁对象可以是任意的//当多线程并发, 有多段代码同时执行时//我们希望下面代码执行的过程中CPU不要切换到其他线程工作System.out.print("黑");System.out.print("马");System.out.print("程");System.out.print("序");System.out.print("员");System.out.print("\r\n");}}public void print2() {                      synchronized(d) {//如果说两块代码想同步的话,那么这两个同步代码块的锁对象必须是同一个锁对象,所以说上面的锁对象是d,这一个锁对象也是d//我们希望下面代码执行的过程中CPU不要切换到其他线程工作System.out.print("传");System.out.print("智");System.out.print("播");System.out.print("客");System.out.print("\r\n");}}
}class Demo{}

同步方法

使用synchronized关键字修饰一个方法,该方法中所有的代码都是同步的,非静态同步函数的锁是当前对象this,静态的同步函数的锁是当前类的字节码对象。

代码演示:

public class Demo_Synchronized {public static void main(String[] args) {//匿名内部类使用局部变量,局部变量前面必须加final修饰,为了延长局部变量的声明周期final Printer2 p = new Printer2();new Thread() {public void run() {while(true) {p.print1();}}}.start();new Thread() {public void run() {while(true) {p.print2();}}}.start();}
}class Printer2 {Demo d = new Demo();//非静态的同步方法的锁对象是神马?//答:非静态的同步方法的锁对象是this//静态的同步方法的锁对象是什么?//是该类的字节码对象public static synchronized void print1() {//同步方法只需要在方法上加synchronized关键字即可System.out.print("黑");System.out.print("马");System.out.print("程");System.out.print("序");System.out.print("员");System.out.print("\r\n");}public static void print2() {synchronized(Printer2.class) {       System.out.print("传");System.out.print("智");System.out.print("播");System.out.print("客");System.out.print("\r\n");}}
}

死锁
同步代码块的嵌套就容易出现死锁。所以开发中尽量避免同步代码块的嵌套。

代码演示:

public class Demo5_DeadLock {/**@param args*/private static String s1 = "筷子左";private static String s2 = "筷子右";public static void main(String[] args) {new Thread() {public void run() {while(true) {synchronized(s1) {System.out.println(getName() + "...获取" + s1 + "等待" + s2);synchronized(s2) {System.out.println(getName() + "...拿到" + s2 + "开吃");}}}}}.start();new Thread() {public void run() {while(true) {synchronized(s2) {System.out.println(getName() + "...获取" + s2 + "等待" + s1);synchronized(s1) {System.out.println(getName() + "...拿到" + s1 + "开吃");}}}}}.start();}
}

Vector,StringBuffer,Hashtable这些类之所以说是同步的,是因为他们里面的方法上都加了synchronized

  • Collections.synchroinzedXxx(xxx):可以返回一个线程安全的集合
  • public static Collection synchronizedCollection(Collection c)
  • public static Set synchronizedSet(Set s)
  • public static List synchronizedList(List list)
  • public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)

注:Vector是线程安全的,ArrayList是线程不安全的

​ StringBuffer是线程安全的,StringBuilder是线程不安全的

​ Hashtable是线程安全的,HashMap是线程不安全的

想了解更多学习知识,请关注微信公众号“阿Q说”,获取更多学习资料吧!你也可以后台留言说出你的疑惑,阿Q将会在后期的文章中为你解答。每天学习一点点,每天进步一点点。

多线程与同步代码块详解相关推荐

  1. Java 代码块详解

    文章目录 Java 代码块详解 基本介绍 基本语法 代码块示例 代码块使用细节 Java 代码块详解 基本介绍 代码块又称 初始化块, 是类的一部分,属于类中的成员,类似于方法,将逻辑语句封装在方法体 ...

  2. Java基础篇:四种代码块详解

    所谓代码块,就是用大括号{}将多行代码封装在一起,形成一个独立的数据体,用于实现特定的算法.一般来说,代码块是不能单独运行的,它必须有运行主体.在Java中代码块主要分为四种:普通代码块.静态代码块. ...

  3. java代码讲解_Java基础系列-代码块详解

    注:本文出自博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 前言 Java基础系列,尽量采用通俗易懂.循序渐进的方式,让大家真正理解Java基础知识! 代码块 ...

  4. Java—代码块详解

    代码块概念 代码块分类 普通代码块 构造代码块 静态代码块 同步代码块 代码块概念 在java中,{ } 括起来的代码被称为代码块 代码块分类 普通代码块 类中方法的方法体 public class ...

  5. 12-Java 继承抽象类代码块(详解~)

    文章目录 1. 继承 1.1 继承的实现(掌握) 1.2 继承的好处和弊端(理解) 1.3. Java中继承的特点(掌握) 2. 继承中的成员访问特点 2.1 继承中变量的访问特点(掌握) 2.2 s ...

  6. java中静态代码块详解

    参考博客:https://blog.csdn.net/qq_36792191/article/details/103988565 1.静态代码块基本知识 **(1)静态代码块:**执行优先级高于非静态 ...

  7. Java 静态代码块详解

    文章目录 一.普通初始化块: 二.静态代码块: 三.多种代码块间的执行顺序 首先得知道什么是初始化块:就是类中得一段用大括号 {} 括起来得独立的代码空间. 而初始化块分成了 普通初始化块和 静态的初 ...

  8. Oracle代码块详解,Oracle可执行代码块

    declare --声明变量 v_menu_name varchar(50):='aaa管理'; --声明字符串变量必须带上长度 v_menu_url varchar(100):='/aaa.html ...

  9. 多线程(并发)买票的案例详解同步代码块和同步方法 (复习)

    文章目录 前言 简单的原理: 队列+锁 常见的买票案例(线程安全:多个线程操作同一个数据.) 结果: 使用同步代码块或者同步方法(解决线程不安全的问题) 添加的方式一:synchronized 关键字 ...

最新文章

  1. python http接口_python处理http接口请求
  2. [30期] 第一个项目
  3. gltexsubimage2d 性能_风水轮流转!AMD R5 5600X单核性能碾压十代i9
  4. python布尔类型运算_9.python的布尔类型与流程控制
  5. html中隐式转换成数字,关于 JS 类型隐式转换的完整总结
  6. 【Vue2.0】— 全局事件总线GlobalEventBus(十九)
  7. [转载] Python基本语法之:字符串和字典介绍
  8. matlab 向量转置,matlab中向量和矩阵怎么转置 值得收藏
  9. UI设计师(界面设计)面试题
  10. github网站下载方法
  11. Redisson分布式锁学习总结:RedissonMultiLock 如何同时锁住N个资源
  12. consol32.exe
  13. 《那些年啊,那些事——一个程序员的奋斗史》——25
  14. ArcGIS山脊线、山谷线和山顶点的提取(附练习数据下载)
  15. pmp中ram和raci的区别_信息系统项目管理师和PMP考试考哪个?
  16. python进程池multiprocessing.Pool和线程池multiprocessing.dummy.Pool实例
  17. 2013ACM/ICPC亚洲区南京站现场赛——题目重现
  18. [知识点整理]中科院/国科大 自然语言处理nlp 期末考试知识点整理
  19. 写给渡边模特的感谢信
  20. 英文文献阅读时,如何做笔记?

热门文章

  1. 微软校招面试题3-15 (一道经典算法,一道比较难的题目)
  2. 用matlab画声波,基于MATLAB的声波分析研究-复旦大学物理教学试验中心.PDF
  3. 一个c加一个g是什么牌子_C和G打头的手表都有哪些?
  4. 珠穆朗玛币王:11月18日学会分辨 拒绝盲从
  5. android 7.0 连接电脑,Android 7.0:有人欢喜有人愁
  6. 深入理解WORD高级排版之模板与加载项
  7. Java实现PDF模板套打(证书)
  8. BeanCurrentlyInCreationException: Error creating bean
  9. 计算机二级Office科目方向软件套组安装指南MS Office+WPS
  10. 读书笔记--Neural Networks and Deep Learning(CH1)