多线程· Runnable 和 Thread

多线程的引入

* A:什么是线程
  * 线程是程序执行的一条路径,一个进程中可以包含多条线程
  * 多线程并发执行可以提高程序的效率,可以同时完成多项工作

* B:多线程的应用场景
  * 红蜘蛛同时共享屏幕给多个电脑
  * 迅雷开启多条线程一起下载
  * QQ同时和多个人一起视频
  * 服务器同时处理多个客户端请求

多线程并行和并发的区别

* 并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也同时进行(需要多核CPU)

* 并发是指两个任务都请求运行,而处理器只能接收一个任务,于是就安排轮流执行这两个任务,由于时间间隔较短,因此用户感觉两个任务同时运行

* 比如:我和两个网友聊天,左手操作一个电脑,右手操作另一个电脑,就叫并行
  * 如果用一台电脑先给一个人发消息,再用这台电脑给另一个人发消息,这就叫并发

Java程序运行的原理和JVM的启动

* A:Java程序运行原理
  * Java命令会启动Java虚拟机,启动JVM就等于启动了一个应用程序,也就是启动了一个进程,
  * 该进程会自动启动一个“主线程”,然后主线程区调用某个类的main方法

* B:JVM的启动
  * JVM的启动至少启动了垃圾线程和主线程,所以是多线程的

package com.heima.thread;

public class Demo1_Thread {
// 证明JVM是多线程的
public static void main(String[] args) {
for (int i = 0; i < 390000; i++) {
new Demo();
}
for (int i = 0; i < 10000; i++) {
System.out.println("我是主线程的执行代码");
}
}
} class Demo { // 垃圾清理线程
@Override
public void finalize() throws Throwable {
System.out.println("垃圾被清扫了");
}
}

thread

Thread 实现多线程

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

package com.heima.thread;

public class Demo2_Thread {

    public static void main(String[] args) {
MyThread mt = new MyThread(); // 创建线程的子类对象
// mt.run(); // 未开启线程
mt.start(); // 使该线程开始执行,开启线程需要时间,类似赛道上的发令枪 for (int i = 0; i < 1000; i++) { // 调用主线程
System.out.println("bb"); // 结果:bb 和 aaaaaa 成块状间隔打印
}
}
} class MyThread extends Thread { // 继承Thread
public void run() { // 重写run方法
for (int i = 0; i < 1000; i++) { // 将想要执行的代码写在run方法中
System.out.println("aaaaaaaaaa");
}
}
}

thread

Runnable 实现多线程

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

package com.heima.thread;

public class Demo3_Thread {

    public static void main(String[] args) {
MyRunnable mr = new MyRunnable(); // 创建Runnable子类对象 // Thread t = new Thread(mr); // 将Runnable的子类当作参数传递给Thread
// t.start(); // 开启线程
new Thread(mr).start(); // 使用匿名类开启线程 for (int i = 0; i < 1000; i++) {
System.out.println("bb"); // 穿插打印 aaaaa 和 bb
} }
} class MyRunnable implements Runnable { // 定义一个类实现Runnable接口
@Override
public void run() { // 重写run方法
for (int i = 0; i < 1000; i++) { // 将想要执行的代码写在run方法中
System.out.println("aaaaaaaaaaaa");
}
} }

Runnable

两种方式的区别

* 查看源码的区别:
  * a:继承 Thread,由于子类重写了 Thread类的 run()方法,当调用 start()时,直接找子类的 run()方法
  * b:实现 Runnable,构造函数中传入了 Runnable的引用,成员变量记住了它,
    start()调用 run()方法时内部判断成员变量 Runnable的引用是否为空,不为空编译时看的是 Runnable的 run(),运行时运行的是子类的 run()方法

* 继承 Thread
  * 好处是:可以直接使用 Thread类中的方法,代码简单
  * 弊端是:如果已经有了父类,就不能用这种方法

* 实现 Runnable接口
  * 好处是:即使自己定义的线程类有了父类也没关系,而且接口是多实现的
  * 弊端是:不能直接使用 Thread中的方法,需要先获取到线程对象后,才能得到 Thread的方法,代码复杂

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

* 继承 Thread类

* 实现 Runnable接口

package com.heima.thread;

public class Demo4_Thread {
// 使用匿名类继承Thread,实现Runnable,来开启多线程
public static void main(String[] args) {
new Thread() { // 继承Thread类
public void run() { // 重写run方法
for (int i = 0; i < 1000; i++) { // 将要执行的代码写在run方法中
System.out.println("aaaaaaa");
}
}
}.start(); // 开启线程 new Thread(new Runnable() { // 实现 Runnable接口
@Override
public void run() { // 重写run方法
for (int i = 0; i < 1000; i++) { // 将要执行的代码写在run方法中
System.out.println("bb");
}
}
}).start(); // 开启线程
}
}

thread

Thread 获取线程名和设置线程名

* A:获取名字
  * 通过 getName()方法获取线程对象的名字

* B:设置名字
  * 通过构造函数传入 String类型的名字
  * 通过 setName(String)方法可以设置线程对象的名字

package com.heima.threadmethod;

public class Demo1_Name {

    public static void main(String[] args) {
// demo1();
// demo2();
} public static void demo2() {
new Thread() {
public void run() {
this.setName("zwb"); // 使用 setName()方法设置线程名,因为是在类的内部,所以用this关键字表示对此类命名
System.out.println(this.getName()+ " aaaaa");
}
}.start(); Thread t1 = new Thread() {
public void run() {
System.out.println(this.getName()+ " bb");
}
};
t1.setName("cly"); // 使用 setName()对线程命名,因为在类外,所以通过对象名调用
t1.start();
} public static void demo1() {
new Thread("zwb") { // 通过构造方法给线程命名
public void run() {
System.out.println(this.getName() + " aaaaaaa"); // 调用 getName()方法获取线程名
}
}.start(); new Thread("cly") { // 通过构造方法给线程命名
public void run() {
System.out.println(this.getName() + " bb"); // 调用 getName()方法获取线程名
}
}.start();
}
}

thread

获取当前线程的对象

* Thread.currentThred(),主线程也可以获取

package com.heima.threadmethod;

public class Demo2_CurrentThread {

    public static void main(String[] args) {
new Thread() {
public void run() {
System.out.println(getName() + " aaaaa");
}
}.start(); new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " bb"); // 获取当前正在执行的线程
}
}).start(); Thread.currentThread().setName("zwb"); // 设置当前正在执行的线程的名字,即主线程
System.out.println(Thread.currentThread().getName()); // 获取主线程的名字
}
}

currentThread

休眠线程

* Thread.sleep(毫秒值,纳秒值),控制当前线程休眠若干毫秒
* 1秒 = 1 000毫秒 = 1 000 000 微秒 = 1 000 000 000 纳秒

package com.heima.threadmethod;

public class Demo3_Sleep {

    public static void main(String[] args) throws InterruptedException {
// demo1();
// demo2();
} public static void demo2() {
new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(30); // 线程睡30毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + " aaaaa");
}
}
}.start(); // 开启线程1 new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(30); // 线程睡30毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + " bb");
}
}
}.start(); // 开启线程2
} public static void demo1() {
for(int i = 20; i >= 0; i--) {
try {
Thread.sleep(1000); // 线程睡1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("倒计时 第" + i + "秒");
}
}
}

sleep

守护线程

* setDaemo(),设置一个线程为守护线程,该线程不会单独执行,当其他非守护线程都执行结束后,自动退出

package com.heima.threadmethod;

public class Demo4_Daemon {
// 非守护线程一旦结束,守护线程也会随之结束
public static void main(String[] args) {
Thread t1 = new Thread("cly") { // 创建线程1
public void run() {
for (int i = 0; i < 2; i++) {
System.out.println(getName() + " aaaa");
}
}
}; Thread t2 = new Thread("zwb") { // 创建线程2
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(getName() + " bb");
}
}
}; t2.setDaemon(true); // 当传入true就是设置为守护线程
t1.start(); // 开启线程
t2.start();
}
}

Daemon

加入线程

* join(),当前线程暂停,等到指定的线程执行结束后,当前线程再继续

* join(int),可以等待指定的毫秒之后继续

package com.heima.threadmethod;

public class Demo5_Join {

    public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + " aaaaaaaa");
}
}
}; // 创建线程1 Thread t2 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
if (i == 2) {
/*try {
t1.join();// t2执行2次之后暂停,等到t1全部执行完之后再让t2执行完剩下的所有
} catch (InterruptedException e) {
e.printStackTrace();
}*/
try {
t1.join(20); // t2执行2次之后暂停,让t1单独先执行20毫秒,之后两条线程再交替执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getName() + " bb");
}
}
}; // 创建线程2 t1.start(); // 开启线程
t2.start();
}
}

join

礼让线程

* yield 让出CPU

package com.heima.threadmethod;

public class Demo6_Yield {

    public static void main(String[] args) {
new MyThread().start(); // 开启线程1
new MyThread().start(); // 开启线程2
}
} class MyThread extends Thread {
public void run() {
for (int i = 0; i < 1000; i++) {
if (i % 10 == 0) { // 判断是否满足条件
Thread.yield(); // yield让出CPU,但真正运行时可能不会让
}
System.out.println(getName() + " " + i);
}
}
}

yield

设置线程优先级

* setPriority(),设置线程的优先级

package com.heima.threadmethod;

public class Demo7_Priority {

    public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + " aaaaaaaa");
}
}
}; // 创建线程1 Thread t2 = new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + " bb");
}
}
}; // 创建线程2 // t1.setPriority(10); // 设置最大优先级,优先级高的相对先执行
// t2.setPriority(1); // 设置最小优先级 t1.setPriority(Thread.MIN_PRIORITY); // 设置最小的线程优先级,1
t2.setPriority(Thread.MAX_PRIORITY); // 设置最大的线程优先级,10 t1.start(); // 开启线程
t2.start(); }
}

Priority

同步代码块

* A:什么情况下需要同步
  * 当多线程并发,有多段代码同时执行时,我们希望某一段代码执行的过程中CPU不要切换到其他线程工作,这时就需要同步
  * 如果两段代码是同步的,那么同一时间只能执行一段,在一段代码没执行结束之前,不会执行另外一段代码

* B:同步代码块
  * 使用 synchronized关键字加上一个锁对象来定义一段代码,这就叫同步代码块
  * 多个同步代码块如果使用相同的锁对象,那么他们就是同步的

package com.heima.syn;

public class Demo1_Synchronized {

    public static void main(String[] args) {
final Printer p = new Printer(); new Thread() {
public void run() {
int i = 0;
while (i < 100) {
p.print1();
i++;
}
}
}.start(); // 开启线程1 new Thread() {
public void run() {
int i = 0;
while (i < 100) {
p.print2();
i++;
}
}
}.start(); // 开启线程2
}
} class Printer {
Demo d = new Demo(); public void print1() {
synchronized (d) { // 同步代码块的锁机制,锁对象可以是任意的对象,但不能是匿名对象
System.out.print("z");
System.out.print("w");
System.out.print("b");
System.out.println();
}
} public void print2() {
// synchronized (new Demo()) { // 不能是匿名对象
synchronized (d) {
System.out.print("c");
System.out.print("l");
System.out.print("y");
System.out.println();
}
}
} class Demo {
}

synchronized

同步方法

* 使用 synchronized关键字修饰一个方法,该方法中的所有代码都是同步的

package com.heima.syn;

public class Demo2_Synchronized {

    public static void main(String[] args) {
Print2 p = new Print2(); Thread t1 = new Thread() {
public void run() {
for (int i = 0; i < 1000; i++) {
p.print1();
}
}
}; // 创建线程1 Thread t2 = new Thread() {
public void run() {
for (int i = 0; i < 1000; i++) {
p.print2();
}
}
}; // 创建线程2 t1.start(); // 开启线程
t2.start();
}
} class Print1 {
// 非静态的同步方法的锁对象是 this
public synchronized void print1() { // 同步方法只需要再方法上加上synchronized即可
System.out.print("z");
System.out.print("w");
System.out.print("b");
System.out.println();
} public void print2() {
synchronized (this) {
System.out.print("c");
System.out.print("l");
System.out.print("y");
System.out.println();
}
}
} class Print2 {
// 静态的同步方法的锁对象是 字节码对象 也就是 Print2.class 或 getClass()
public static synchronized void print1() {
System.out.print("z");
System.out.print("w");
System.out.print("b");
System.out.println();
} public void print2() {
synchronized (getClass()) {
System.out.print("c");
System.out.print("l");
System.out.print("y");
System.out.println();
}
}
}

synchronized

线程安全问题

* 多线程并发操作同一数据时,就有可能出现线程安全问题
* 使用绒布技术可以解决这种问题,把操作数据的代码进行同步,不要多个线程一起操作

* 举例:铁路售票

package com.heima.syn;

public class Demo3_Ticket {

    public static void main(String[] args) {
new Ticket("1").start(); // 开启线程
new Ticket("2").start();
new Ticket("3").start();
new Ticket("4").start();
}
} class Ticket extends Thread {
public Ticket() {
super();
} public Ticket(String name) { // 有参构造,给线程命名
super(name);
} private static int ticket = 1000; // 定义静态的票数,使得变量是公共的
private static Object o = new Object(); // 如果用引用数据类型成员变量当作锁对象,必须是静态的,随着类的加载而加载,也是公共的 public void run() { // 重写 run()方法
while (true) { // 定义无限循环
synchronized (Ticket.class) { // 用字节码对象当作锁对象
if (ticket == 0) { // 判断条件跳出
break;
} try {
Thread.sleep(1); // 线程1睡,线程2睡,线程3睡,线程4睡
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println(getName() + " 这是第" + ticket-- + "号票");
}
}
}
}

继承Thread

死锁问题

* 多线程同步的时候,如果同步代码嵌套,使用相同的锁,就有可能出现死锁
  * 尽量不要嵌套使用

package com.heima.syn;

public class Demo5_DeadLock {
private static String s1 = "筷子左"; // 锁1
private static String s2 = "筷子右"; // 锁2 public static void main(String[] args) {
new Thread() {
public void run() { // 重写 run()方法
for (int i = 0; i < 100; i++) {
synchronized (s1) {
System.out.println(getName() + ":获取" + s1 + " 等待" + s2);
synchronized (s2) {
System.out.println(getName() + ":拿到" + s2 + "开吃");
}
}
}
}
}.start(); // 开启线程1 new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
synchronized (s2) {
System.out.println(getName() + ":获取" + s2 + " 等待" + s1);
synchronized (s1) {
System.out.println(getName() + ":拿到" + s1 + "开吃");
}
}
}
}
}.start(); // 开启线程2
}
}

DeadLock

以前的线程安全类的回顾

* A:回顾以前说过的线程安全问题
  * 看源码:Vector,StringBuffer,Hashtable,Collections.synchronized(xxx)

  * Vector 是线程安全的,ArrayList 是线程不安全的
  * StringBuffer 是线程安全的,StringBuilder 是线程不安全的
  * Hashtable 是线程安全的,HashMap 是线程不安全的
  * Collections.synchronized(xxx),可以将线程不安全的变成线程安全的

Java 多线程 01的更多相关文章

  1. Java多线程| 01 | 线程概述

    Java多线程| 01 | 线程概述 线程相关概念 进程与线程 进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位.可以把进程简单的理解 ...

  2. Java多线程01(Thread类、线程创建、线程池)

    Java多线程(Thread类.线程创建.线程池) 第一章 多线程 1.1 多线程介绍 1.1.1 基本概念 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于 ...

  3. Java多线程系列--“JUC锁”01之 框架

    本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...

  4. java多线程系类:JUC锁:01之框架

    本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--"JUC锁"01之 框架02. Java多线程系列--"JUC锁&q ...

  5. Java多线程系列--“基础篇”01之 基本概念

    多线程是Java中不可避免的一个重要主体.从本章开始,我们将展开对多线程的学习.接下来的内容,是对“JDK中新增JUC包”之前的Java多线程内容的讲解,涉及到的内容包括,Object类中的wait( ...

  6. Java多线程系列--“JUC集合”01之 框架

    概要 之前,在"Java 集合系列目录(Category)"中,讲解了Java集合包中的各个类.接下来,将展开对JUC包中的集合进行学习.在学习之前,先温习一下"Java ...

  7. java多线程系类:基础篇:01基本概念:

    这个系类的内容全部来源于http://www.cnblogs.com/skywang12345/p/3479024.html.特别在此声明!!! 本来想直接看那位作家的博客的,但还是复制过来. 多线程 ...

  8. java多线程系类:JUC线程池:01之线程池架构

    概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容--线程池.内容包括:线程池架构 ...

  9. Java多线程系列--“JUC线程池”01之 线程池架构

    概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容——线程池.内容包括:线程池架构 ...

随机推荐

  1. STL中pair容器的用法

    1.定义pair容器 1 pair <int, int> p, p1; 2 //定义 [int,int] 型容器 //直接初始化了p的内容 pair<string,int>p( ...

  2. Codeforces Round #680 (Div. 2, based on Moscow Team Olympiad) D. Divide and Sum (思维,数学,逆元)

    题意:有一个长度为\(2n\)数组,从中选分别选\(n\)个元素出来组成两个序列\(p\)和\(q\),(\(p\)和\(q\)中只要有任意一个元素在\(a\)的原位置不同,就算一个新的情况),选完后 ...

  3. hdu5375 Gray code

    Problem Description The reflected binary code, also known as Gray code after Frank Gray, is a binary ...

  4. C++构造函数、复制函数易错点

    C++中复制函数在三种情况下自动调用: 用一个对象初始化另一个对象 函数的参数为对象 函数的返回值为对象 下面用几个代码片段解释复制函数的调用中的一些常见"坑": 一:默认复制函数 ...

  5. Python小练习批量爬取下载歌曲

    import requests import os headers={ 'Cookie': '_ga=GA1.2.701818100.1612092981; _gid=GA1.2.748589379. ...

  6. bochs 调试 com 文件 magicbreak

    参考 https://blog.csdn.net/housansan/article/details/41833581 在网上看到2中解决此问题的方法:1.使用dos下的debug32工具单步跟踪pm ...

  7. 线程同步之信号量(sem_init,sem_post,sem_wait)

    信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区. 不多做解释,要使用信号量同步,需要包含头文件semaphore.h. 主要用到的函数: int ...

  8. Python+OpenCV+图片旋转并用原底色填充新四角

    import cv2 from math import fabs, sin, cos, radians import numpy as np from scipy.stats import mode ...

  9. how to remove duplicates of an array by using js reduce function

    how to remove duplicates of an array by using js reduce function ??? arr = ["a", ["b& ...

  10. npm ci All In One

    npm ci All In One npm 性能优化 npm ci 使用干净的面板安装项目 https://docs.npmjs.com/cli/v6/commands/npm-ci # npm cl ...