多线程· 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. Complete the sequence! POJ - 1398 差分方法找数列规律

    参考链接:http://rchardx.is-programmer.com/posts/16142.html vj题目链接:https://vjudge.net/contest/273000#stat ...

  2. HDU - 6030 矩阵快速幂 +多组输入快速幂板子

    题意:一个项链用n个珠子构成,是一个条而不是一个环,由红和蓝两种颜色构成,要求以任意点为起点向后的素数个珠子中,保证红颜色的大于等于蓝颜色的,问你有多少种方案满足,范围:n(2≤n≤1018) 推导过 ...

  3. SQL Server的嵌套存储过程中使用同名的临时表怪像浅析

      SQL Server的嵌套存储过程,外层存储过程和内层存储过程(被嵌套调用的存储过程)中可以存在相同名称的本地临时表吗?如果可以的话,那么有没有什么问题或限制呢? 在嵌套存储过程中,调用的是外层存 ...

  4. Java对象延迟初始化的实现

    一.什么是延迟初始化? 在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销. 延迟初始化实际上就是:当我们要进行一些高开销的对象初始化操作时,只有在使用这些对象时才进行初始 ...

  5. Python——Django框架——Form框架

    一.引入Form包 from django import forms 二.定义规则 class Forms_Login(forms.Form): 用户名 = forms.CharField(requi ...

  6. LINUX - 文件读写缓存

    遇到一个进程core掉后日志打印不出来的问题: 参考如下: [引用] 只有正常退出,才能做到flush.否则将写失败. 之后有百度了下中文资料,发现同样的结论. "fflush库函数的作用是 ...

  7. Redis性能指标监控

    监控指标 •性能指标:Performance•内存指标: Memory•基本活动指标:Basic activity•持久性指标: Persistence•错误指标:Error 性能指标:Perform ...

  8. String的20个方法

    String的20个方法 面试题 1.new和不new的区别 String A="OK"; String B="OK";//会去常量池查找有没有"Ok ...

  9. C# 类 (8) - 抽象方法

    抽象 抽象方法 只能 定义在抽象类 里,并且抽象方法里没有具体的代码,像这种 为啥要定义一个空空如也的函数呢?这是为了用来约束 它的派生类 的行为, 这个例子,建立了一个数组,放了cat和dog,这两 ...

  10. C++ part1

    C++内存分配 references: C++ 自由存储区是否等价于堆? c++ new 与malloc有什么区别 C++, Free-Store vs Heap 1. 栈:由编译器自动分配释放,存放 ...