Java 基础篇:第十九章:多线程
主要内容:
- 多线程基础和原理
- Thread类和Runnable接口
- Thread常见方法
- 线程同步
补充Properties两个问题:
1、是Hashtable的子类,可以直接使用父类的put(K key,V value)方法,但是一般不推荐
使用。因为我们在使用Properties的对象时,属性列表中每个键及其对应值都是一个字符串。一般尽量使用(String key, String value)。
2、Properties本身内部是无序的,但是在执行store()方法时,方法内部使用了枚举,最终生成的文件是有序的。
一、多线程基础和原理:
1.1 多线程的引入:
线程:是程序执行的一条路径,一个进程可以包含多个线程。进程是一个动态的概念,线程是一个静态的概念。
多线程并发执行可以提高程序的效率,可以同时完成多项工作。
多线程的应用场景:QQ同时与多人视频或共享桌面、服务器同时处理多个客户端的请求、使用迅雷开启多个线程一起下载......
1.2 多线程并行和并发:
多线程并发:多个任务都要请求运行,而处理器只能接受一个任务,就把这些任务分时段轮流进行,由于时间非常短暂,使人感觉是多个任务同时运行。
多线程并行:就是两个(或以上)的任务同时运行,在某一个进程运行过程中,有其他的进程也在同时进行。(前提是需要多个CPU)
1.3 JVM的运行:
Java程序运行原理:Java命令会开启JVM,启动JVM进程,相当于启动了一个应用程序。该进程会启动一个“主线程”,然后主线程去调用某个类中main方法。
JVM本身是不是多线程?
public class Demo01_Thread { /* * 判断JVM是不是多线程?——JVM是多线程的。 */ public static void main(String[] args) { // main方法本身是由主线程调用的。 for (int i = 0; i < 1000000; i++) { // 开启了自定义的一个线程 new Demo(); } for (int j = 0; j < 10; j++) { System.out.println("我是主线程的执行代码..."); } } } class Demo { // Object类finalize() @Override public void finalize() { System.out.println("垃圾被成功地清除了!!!!!"); } } |
1.4 多线程的实现方法(Thread):
public class Demo02_Thread { /** * 使用Thread类实现多线程 */ public static void main(String[] args) { // 4.创建一个Thread的子类对象(线程) MyThread thread = new MyThread(); // 5.开启线程 thread.start(); for (int i = 0; i < 1000; i++) { // 主线程的代码 System.out.println("Nature & Peace"); } } } class MyThread extends Thread { // 1.继承Thread @Override // 注解 public void run() { // 2.重写run方法 for (int i = 0; i < 1000; i++) { // 3.将要执行的代码,写在run中 System.out.println("相信自己"); } } } |
1.5 多线程的实现方式(Runnable):
public class Demo03_Thread { /** * 继承Runnable接口,来实现多线程 */ public static void main(String[] args) { // 4.创建MyRunnable的对象 MyRunnable myRunnable = new MyRunnable(); // 5.开启线程 Thread thread = new Thread(myRunnable); thread.start(); // 主线程的代码 for (int i = 0; i < 1000; i++) { System.out.println("Nature & Peace."); } } } class MyRunnable implements Runnable { // 1.定义一个类实现Runnable接口 @Override public void run() { // 2.实现接口中的run()方法 for (int i = 0; i < 1000; i++) { // 3.将要执行的代码写在run方法中 System.out.println("相信自己"); } } } |
1.6 两种实现方式的区别:
1、继承Thread类:由于子类重写Thread类的run()方法,当我们执行start()方法时,直接找子类run()方法。编译的时候,调用Runnable接口中的run()方法声明。
2、实现Runnable接口:Thread构造函数中传入Runnable接口对应的一个实例对象的引用,成员变量获得该引用,start()方法调用run()方法时内部判断成员变量Runnable的引用是否为空,不空编译时看Runnable中的run()方法声明,运行时执行的是实现类中的run()方法。
1.7 匿名内部类实现多线程:
public class Demo04_Thread { /** * 使用内部类实现多线程 */ public static void main(String[] args) { new Thread() { // 1.继承Thread类(使用的是匿名类) @Override public void run() { // 2.重写run()方法 for (int i = 0; i < 1000; i++) { // 3.将要执行的代码,写在run中 System.out.println("相信自己"); } } }.start(); // 4.开启线程 // 使用内部类+Runnable接口实现多线程 new Thread(new Runnable() { // 1.使用匿名类写一个Runnable接口的实例对象,传给Thread的构造方法 @Override public void run() { // 2.实现run()方法 for (int i = 0; i < 1000; i++) { // 3.要执行的代码放在run()方法中 System.out.println("Nature & Peace."); } } }).start(); // 4.开启线程 // for (int i = 0; i < 1000; i++) { // 主线程的代码 // System.out.println("Nature & Peace."); // } } } |
- Thread类的常用方法:
2.1 获取名字和设置名字:
public class Demo01_Name { /** * 线程的名称:getName()/setName(String name) */ public static void main(String[] args) { // demo01(); Thread thread1 = new Thread() { // 1.继承Thread(Thread类的对象thread1指向Thread类的一个子类匿名对象的引用) @Override public void run() { // 2.重写run()方法 for (int i = 0; i < 1000; i++) { // 3.run方法中的代码 // 此处的this代表当前的这Thread子类,使用getName()方法获取线程名称 System.out.println(this.getName() + "----------Do trust me!"); } } }; Thread thread2 = new Thread() { // 1.继承Thread @Override public void run() { // 2.重写run()方法 for (int i = 0; i < 1000; i++) { // 3.run方法中的代码 System.out.println(this.getName() + "..........请相信我!"); } } }; thread1.setName("铁拐李"); // 4.设置线程的名称 thread2.setName("猪悟能"); thread1.start(); // 5.启动线程 thread2.start(); } /** * */ private static void demo01() { new Thread("铁拐李") { // 1.继承Thread(调用构造方法设置线程的名称) @Override public void run() { // 2.重写run()方法 for (int i = 0; i < 1000; i++) { // 3.run方法中的代码 // 此处的this代表当前的这Thread子类,使用getName()方法获取线程名称 System.out.println(this.getName() + "----------Do trust me!"); } } }.start(); // 4.启动线程 new Thread("二师兄") { // 1.继承Thread @Override public void run() { // 2.重写run()方法 for (int i = 0; i < 1000; i++) { // 3.run方法中的代码 System.out.println(this.getName() + "..........请相信我!"); } } }.start(); // 4.启动线程 } } |
2.2 获取当前线程的对象:
public class Demo02_CurrentThread { /** * public static Thread currentThread():返回对当前正在执行的线程对象的引用。 */ public static void main(String[] args) { new Thread() { public void run() { System.out.println(getName() + ".......Just do it!"); } }.start(); new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "--------干就完了。"); } }).start(); // Thread.currentThread().setName("我是主线程"); System.out.println(Thread.currentThread().getName()); } } |
2.3 休眠线程:
public static void sleep(long millis) throws InterruptedException:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
public class Demo03_Sleep { /** * sleep(long millis)方法,线程休眠 */ public static void main(String[] args) throws InterruptedException { // demo01(); new Thread() { // 继承了Thread类 public void run() { for (int i = 0; i < 1000; i++) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + "--------Just do it."); } } }.start(); new Thread() { public void run() { for (int i = 0; i < 1000; i++) { try { Thread.sleep(450); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName() + ".........干就完了!"); } } }.start(); } /** * sleep方法实现倒计时 */ private static void demo01() throws InterruptedException { for (int i = 20; i > 0; i--) { Thread.sleep(1000); // 1000ms=1s System.out.println("敌军将在" + i + "秒内到达战场!"); } } } |
2.4 守护线程:
public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。
public class Demo04_Daemon { /** * 守护线程setDaemon(boolean on) */ public static void main(String[] args) { Thread thread1 = new Thread() { public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName() + "........你愁啥?" + i); } } }; Thread thread2 = new Thread() { public void run() { for (int i = 0; i < 1000; i++) { System.out.println(getName() + "........瞅你咋滴?" + i); } } }; thread2.setDaemon(true); // 设置为守护线程,被守护的是thread1 // 当thread1线程完全执行结束之后,thread2应该立即结束;实际上thread2的结束会有非常小的时间延迟 thread1.start(); thread2.start(); } } |
2.5 加入线程:
public final void join(long millis) throws InterruptedException:等待该线程终止的时间最长为 millis 毫秒。超时为 0 意味着要一直等下去。
public class Demo05_Join { /** * 线程加入:join(long millis) */ public static void main(String[] args) { // 匿名内部类要访问方法的局部变量,该变量必须使用final修饰。 final Thread thread1 = new Thread() { public void run() { for (int i = 0; i < 15; i++) { System.out.println(getName() + "--------Just do IT."); } } }; Thread thread2 = new Thread() { public void run() { for (int i = 0; i < 15; i++) { if (i == 5) { try { thread1.join(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(getName() + ".........学好IT!"); } } }; thread1.start(); thread2.start(); } } |
2.6 礼让线程:
public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
public class Demo06_Yield { /** * yield()方法让出CPU资源,交给其他线程 */ public static void main(String[] args) { new MyThread().start(); new MyThread().start(); } } class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 1000; i++) { if (i % 10 == 0) { Thread.yield(); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(getName() + "-------" + i); } } } |
2.7 线程优先级:
public final static int MIN_PRIORITY = 1; 最低优先级
public final static int NORM_PRIORITY = 5; 默认优先级
public final static int MAX_PRIORITY = 10; 最高优先级
public class Demo07_Priority { /** * 线程的优先级 */ public static void main(String[] args) { Thread thread1 = new Thread() { public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName() + ".......二师兄,你好!"); } } }; Thread thread2 = new Thread() { public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName() + ".......沙师弟!"); } } }; thread1.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级(10) thread2.setPriority(Thread.MIN_PRIORITY); // 设置最低优先级(1) thread1.start(); thread2.start(); } } |
三、线程同步:
3.1 同步代码块:
同步代码块,根据锁机制,可以是任意对象,必须是同一个对象。
不能使用匿名对象,因为匿名对象不是同一个对象
锁对象可以直接使用this关键字,前提是这两个方法必须是同一个类的成员方法(非static)。
public class Demo01_Synchronized { /** * 同步代码块 */ public static void main(String[] args) { final Printer printer = new Printer(); new Thread() { public void run() { while (true) { printer.print01(); } } }.start(); new Thread() { public void run() { while (true) { printer.print02(); } } }.start(); System.out.println("aaa"); } } class Printer { Demo demo = new Demo(); public void print01() { // 同步代码块,根据锁机制,可以是任意对象,必须是同一个对象。 // 不能使用匿名对象,因为匿名对象不是同一个对象 // 锁对象可以直接使用this关键字,前提是这两个方法必须是同一个类的成员方法(非static) // synchronized (demo) { synchronized (this) { System.out.print("相"); System.out.print("信"); System.out.print("自"); System.out.print("已"); System.out.print("\r\n"); } } public void print02() { // synchronized (demo) { synchronized (this) { System.out.print("坚"); System.out.print("持"); System.out.print("努"); System.out.print("力"); System.out.print("吧"); System.out.print("\r\n"); } } } class Demo {} |
3.2 同步方法:
非静态的同步方法的锁对象是this
静态的同步方法的锁对象是该类的字节码对象
public class Demo02_SynchronizedFunc { /** * 同步代码块 */ public static void main(String[] args) { final Print prt = new Print(); new Thread() { public void run() { while (true) { prt.print01(); } } }.start(); new Thread() { public void run() { while (true) { prt.print03(); } } }.start(); System.out.println("aaa"); } } class Print { // 非静态的同步方法的锁对象就是this // 静态的同步方法的锁对象是字节码文件(Print.class) public static synchronized void print01() { System.out.print("相"); System.out.print("信"); System.out.print("自"); System.out.print("已"); System.out.print("\r\n"); } // public static synchronized void print02() { // System.out.print("坚"); // System.out.print("持"); // System.out.print("努"); // System.out.print("力"); // System.out.print("吧"); // System.out.print("\r\n"); // } // 如果print01()方法被定义为非静态的同步方法,那么此时应该使用this作为同步锁 // 如果print01()方法被定义为静态的同步方法,那么此时应该使用该类的字节码(Print.class)作为同步锁 public void print03() { synchronized (Print.class) { System.out.print("坚"); System.out.print("持"); System.out.print("努"); System.out.print("力"); System.out.print("吧"); System.out.print("\r\n"); } } } |
3.3 线程安全问题:
1、通过集成Thread类实现:
public class Demo03_SafetyTicket { /** * 线程安全问题——火车票 */ public static void main(String[] args) { new TicketThread().start(); new TicketThread().start(); new TicketThread().start(); new TicketThread().start(); } } class TicketThread extends Thread { // 多个线程中使用同一个变量时,该变量必须使用static private static int number = 100; @Override public void run() { while (true) { synchronized (TicketThread.class) { if (number == 0) { break; } try { Thread.sleep(10); // 线程0睡、线程1睡、线程2睡、线程3睡 } catch (InterruptedException e) { e.printStackTrace(); } System.out .println(getName() + "------这是第" + number-- + "号火车票!"); } } } } |
2、通过实现Runnable接口来实现
public class Demo04_SafetyTicket { /** * @param args */ public static void main(String[] args) { MyTicket mt = new MyTicket(); new Thread(mt).start(); new Thread(mt).start(); new Thread(mt).start(); new Thread(mt).start(); } } class MyTicket implements Runnable { private int number = 100; @Override public void run() { while (true) { synchronized (this) { if (number == 0) { break; } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "......这是第" + number-- + "号火车票!"); } } } } |
3.4 多线程的死锁:
public class Demo05_DeadLock { private static String str1 = "左筷子"; private static String str2 = "右筷子"; // 做开发时,千万不要使用锁嵌套 public static void main(String[] args) { new Thread() { public void run() { while (true) { synchronized (str1) { System.out.println(getName() + "......获取到【" + str1 + "】,等待【" + str2 + "】"); synchronized (str2) { System.out.println(getName() + "------只要拿到了【" + str2 + "】就开吃!!!!!"); } } } } }.start(); new Thread() { // 第二个线程 public void run() { while (true) { synchronized (str2) { System.out.println(getName() + "......获取到【" + str2 + "】,等待【" + str1 + "】"); synchronized (str1) { System.out.println(getName() + "------只要拿到了【" + str1 + "】就开吃!!!!!"); } } } } }.start(); } } |
Java 基础篇:第十九章:多线程相关推荐
- 鸟哥的Linux私房菜(基础篇)- 第十九章、认识与分析登录文件
第十九章.认识与分析登录文件 最近升级日期:2009/09/14 当你的 Linux 系统出现不明原因的问题时,很多人都告诉你,你要查阅一下登录文件才能够知道系统出了什么问题了,所以说,了解登录文件是 ...
- Java基础学习——第十六章 Java8新特性
Java基础学习--第十六章 Java8 新特性 Java8(JDK8.0)较 JDK7.0 有很多变化或者说是优化,比如 interface 里可以有静态方法和默认方法,并且可以有方法体,这一点就颠 ...
- Java基础学习——第十四章 网络编程
Java基础学习--第十四章 网络编程 一.网络编程概述 计算机网络: 把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规模大.功能强的网络系统,从而使众多的计算机可以方便地互相传递信 ...
- 鸟哥的Linux私房菜(基础篇)- 第十六章、例行性工作排程 (crontab)
第十六章.例行性工作排程 (crontab) 最近升级日期:2009/09/11 学习了基础篇也一阵子了,你会发现到为什么系统常常会主动的进行一些任务?这些任务到底是谁在配置工作的?如果你想要让自己设 ...
- 鸟哥的Linux私房菜(基础篇)- 第十四章、Linux 账号管理与 ACL 权限配置
第十四章.Linux账号管理与 ACL 权限配置 最近升级日期:2009/09/09 要登陆 Linux 系统一定要有账号与口令才行,否则怎么登陆,您说是吧?不过,不同的使用者应该要拥有不同的权限才行 ...
- map根据value值排序_凯哥带你从零学大数据系列之Java篇---第十九章:集合(Map+Collections)...
温馨提示:如果想学扎实,一定要从头开始看凯哥的一系列文章(凯哥带你从零学大数据系列),千万不要从中间的某个部分开始看,知识前后是有很大关联,否则学习效果会打折扣. 系列文章第一篇是拥抱大数据:凯哥带你 ...
- 鸟哥的Linux私房菜(基础篇)- 第二十六章、Linux 核心编译与管理
第二十六章.Linux核心编译与管理 最近升级日期:2009/09/18 我们说的 Linux 其实指的就是核心 (kernel) 而已.这个核心控制你主机的所有硬件并提供系统所有的功能,所以说,他重 ...
- 鸟哥的Linux私房菜(基础篇)- 第二十五章、 Linux 备份策略
第二十五章. Linux备份策略 最近升级日期:2009/09/18 万一不幸你的 Linux 被黑客入侵了.或是你的 Linux 系统由於硬件关系 (不论是天灾还是人祸) 而挂掉了!这个时候,请问如 ...
- 鸟哥的Linux私房菜(基础篇)- 第二十四章、 X Window 配置介绍
第二十四章. X Window 配置介绍 最近升级日期:2009/08/07 在 Linux 上头的图形介面我们称之为 X Window System,简称为 X 或 X11 罗!为何称之为系统呢?这 ...
- 鸟哥的Linux私房菜(基础篇)- 第十五章、磁碟配额(Quota)与进阶文件系统管理
第十五章.磁碟配额(Quota)与进阶文件系统管理 最近升级日期:2009/09/10 如果您的 Linux 服务器有多个用户经常存取数据时,为了维护所有使用者在硬盘容量的公平使用,磁碟配额 (Quo ...
最新文章
- 2010 年 360 盗取用户密码事件始末
- linux 崩溃文件 coredump 简介
- 数据集合 oracle,oracle集合
- 万物之始正则表达式全解析三部曲(上篇)-正则表达式基础知识及语法
- 备份mysql数据库以及文件--脚本
- XJOJ - 路径数(最短路+最短路路径数量)
- 映射文件中增删改查标签中的parameterType和resultType
- 云服务器安装其他版本系统,云服务器安装自己的系统版本
- mysql8.0.11创建用户报错_mysql8.0创建用户授予权限报错解决方法
- ipython介绍及使用
- LayIM 3.9.1与ASP.NET SignalR实现Web聊天室快速入门(一)之效果展示与关键技术简介
- android 解码 gif 时间,Android 平台实现Gif 图像解码并播放代码及组件
- JQuery源码分析 - 闭包机制在jQuery中的使用及冲突解决
- Java—初识Java与开发环境的安装
- 回归预测 | MATLAB实现贝叶斯优化门控循环单元(BO-GRU)多输入单输出
- 怎样学好英语?(多年英语学习经验总结)
- MAXENT模型的生物多样性生境模拟
- jQuery 流星雨特效
- linux系统python3环境下安装pymysql模块
- Qt 系统下的qm翻译文件