继承Thread的方式实现多线程

public class TestThread extends Thread{
@Override
public void run() {
System.out.println("多线程运行的代码");
for(int i = 0; i < 5; i++){
System.out.println("这是多线程的逻辑代码:" + i);
}
}
}
-------------------------------
public class Test {
public static void main(String[] args) {
Thread t0 = new TestThread();// 继承Thread类的线程
t0.start();// 启动线程,开始运行run方法中的代码
System.out.println("---------------1");
System.out.println("---------------2");
System.out.println("---------------3");
//Thread t1 = new TestThread();
//t1.start();
//Thread t2 = new TestThread();
//t2.start();
}
}

解析

  • 多次运行这个main方法之后 我们发现main方法中打印的3行与开启线程运行run方法中的打印语句是混合起来
  • 而且main方法中的打印与run方法中打印语句顺序是不固定的 为什么呢?
  • main执行t0.start()方法开启多线程之后,就相当于在main方法之外开启一个支流
  • 这个个时候t0.start()的之后的main方法的其他代码的运行就与run方法运行无关了 以当前代码为例
  • t0.start()的之后的main方法的其他代码与run方法的代码并行运行 就像两条河流一样,各走各的
  • 那么控制台输出的结果就是两条并行程序的运行结果总和,这个结果需要拆开成两部分看 就可以看到,各自是保持自己输出顺序
  • 这个就是多线程的异步,这个异步相对于执行t0.start()的主程序来说的
  • 简单来说开启了线程之后run方法中运行的代码主程序中t0.start()之后的程序是并行执行的,没先后关系,这个叫异步

通过实现Runnable接口方式实现多线程

public class TestRunnable implements Runnable{

    int count = 0;
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":Runnable多线程运行的代码");
for(int i = 0; i < 5; i++){
count++;
System.out.println(Thread.currentThread().getName() + ":这是Runnable多线程的逻辑代码:" + count);
}
} }
-------------------------------
public class Test {
public static void main(String[] args) {
Thread t3 = new Thread(new TestRunnable());
t3.start();
//Thread t4 = new Thread(new TestRunnable(), "t-1");
//t4.start();
//Thread t5 = new Thread(new TestRunnable(), "t-2");
//t5.start();
Runnable run = new TestRunnable();
Thread t4 = new Thread(run, "t-1");// t-1线程名称
t4.start();
Thread t5 = new Thread(run, "t-2");
t5.start();
}
}

区别:Thread与Runnable

  • 继承Thread:线程代码存放Thread子类run方法中。重写run方法
  • 实现Runnable:线程代码存在接口的子类的run方法。实现run方法
  • 实现方法的好处
    • 避免了单继承的局限性
    • 多个线程可以共享同一个接口实现类的对象,非常适合,多个线程可以用来处理同一份资源
    • 一般使用实现接口方式来实现多线程
package day15;

public class Test1 {
public static void main(String[] args) {
TestRun run0 = new TestRun();
TestRun run1 = new TestRun(); Thread t0 = new Thread(run0); Thread t1 = new Thread(run1); t0.setName("线程t-0");// 设置线程的名称
t1.setName("线程t-1");// 设置线程的名称 t0.setPriority(1);// 设置线程的优先级
t1.setPriority(10);// 设置线程的优先级 t0.start();
t1.start();
//System.out.println(t0.getName());//如果在创建线程的时候没有指定名称,系统会给出默认名称,通过getName()获取线程名称
//System.out.println(t1.getName()); /**
* 线程的优先级,就是哪个线程有较大个概率被执行 优先级是用数组1-10表示,数字越大优先级越高,如果没有设置默认优先级是5
*/ //System.out.println("t0的优先级:" + t0.getPriority());//获取线程的优先级 System.out.println("---------------1");
System.out.println("---------------2"); System.out.println(t1.isAlive());// 判断当前的线程是否存活 t1.stop();// 强制线程生命期结束,强制停止此线程 try {
t0.join();// 相当于在这块把t0的run的代码插入到这个位置执行
/**
* 专业的说法 就是阻塞当前的main方法,先不执行System.out.println("---------------3");代码
* 先执行join进来的线程的代码
* join的线程执行完毕之后继续执行之前main方法阻塞的代码System.out.println("---------------3");
*/
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("---------------3"); System.out.println(t1.isAlive());
}
} class TestRun implements Runnable {
int count = 0; @Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":Runnable多线程运行的代码");
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);//当前线程睡眠1000毫秒
//相当于当前的这个循环每隔1000毫秒执行一次循环
} catch (InterruptedException e) {
e.printStackTrace();
} if (i % 2 == 0) {
Thread.yield();// 线程让步
} count++;
System.out.println(Thread.currentThread().getName() + ":这是Runnable多线程的逻辑代码:" + count);
}
} }

微信与支付宝支付案例

package day15;

public class Test2 {
public static void main(String[] args) {
//定义账户对象
Acount a = new Acount();
Acount a1 = new Acount(); //多线程对象
User u_weixin = new User(a, 2000);
User u_zhifubao = new User(a, 2000); Thread weixin = new Thread(u_weixin,"微信");
Thread zhifubao = new Thread(u_zhifubao,"支付宝"); weixin.start();
zhifubao.start();
}
} class Acount{
public static int money = 3000;//全局变量,所有的操作共享这个变量 /**
* 提款,判断账户钱够不够
* 多线程调用这个方法,就有问题,线程共享资源时,一个线程在执行这个方法没有完毕时,另一个线程又开始执行这个方法
* 解决思路:显然一个线程整体执行完这个方法,另一个线程再执行
* 通过synchronized同步锁来完成
* 可以直接在方法上加上synchronized关键字
* 在普通方法上加同步锁synchronized,锁的是整个对象,不是某一个方法
* 不同的对象就是不同的锁,普通方法加synchronized,线程使用不同的此方法的对象,还有共享资源的问题
*
* 普通方法加同步锁,锁的当前方法对应的对象,当前的对象的所有加了同步锁的方法是共用一个同步锁
* @param m
*/
public synchronized void drawing(int m){
String name = Thread.currentThread().getName(); if(money < m){
System.out.println(name + "操作,账户金额不足:" + money);
}else{
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m); System.out.println(name + "操作,取款操作:原金额" + money + " - " + "取款金额" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
} } public synchronized void drawing1(int m){
String name = Thread.currentThread().getName(); if(money < m){
System.out.println(name + "操作,账户金额不足:" + money);
}else{
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m); System.out.println(name + "操作,取款操作:原金额" + money + " - " + "取款金额" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
} } /**
* 静态的方法加synchronized,对于所有的对象都是使用同一个一个锁
* @param m
*/
public static synchronized void drawing2(int m){
String name = Thread.currentThread().getName(); if(money < m){
System.out.println(name + "操作,账户金额不足:" + money);
}else{
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m); System.out.println(name + "操作,取款操作:原金额" + money + " - " + "取款金额" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
} } /**
* 对代码块加入同步锁
* 代码块synchronized(this),所有当前的对象的synchronized(this)同步的的代码都是使用同一个锁
* @param m
*/
public void drawing3(int m){
synchronized(this){//表示当前的对象的代码块被加了synchronized同步锁
//用this锁代码块是代表当前的对象,如果在其他方法中也有synchronized(this)的代码块使用的都是同一个同步锁
String name = Thread.currentThread().getName(); if(money < m){
System.out.println(name + "操作,账户金额不足:" + money);
}else{
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m); System.out.println(name + "操作,取款操作:原金额" + money + " - " + "取款金额" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
} public void drawing4(int m){
synchronized(this){//表示当前的对象的代码块被加了synchronized同步锁
//用this锁代码块是代表当前的对象,如果在其他方法中也有synchronized(this)的代码块使用的都是同一个同步锁
String name = Thread.currentThread().getName(); if(money < m){
System.out.println(name + "操作,账户金额不足:" + money);
}else{
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m); System.out.println(name + "操作,取款操作:原金额" + money + " - " + "取款金额" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
}
}
} /**
* synchronized修饰代码块,想要根据不同的对象有不同的锁
* synchronized(a),这个小括号中传入不同的对象就是不同的锁
* @param m
*/
public void drawing5(int m,Acount a){
synchronized(a){//表示通过方法的参数传递进来的对象的代码块被加了synchronized同步锁
//不同的对象就有不同的同步锁
String name = Thread.currentThread().getName(); //如果是微信操作的,先不执行,等支付宝操作,支付宝操作完,微信再继续操作
if(name.equals("微信")){
try {
a.wait();//当前的线程进入等待的阻塞状态
} catch (InterruptedException e) {
e.printStackTrace();
}
} if(money < m){
System.out.println(name + "操作,账户金额不足:" + money);
}else{
System.out.println(name + "操作,账户原有金额:" + money);
System.out.println(name + "操作,取款金额:" + m); System.out.println(name + "操作,取款操作:原金额" + money + " - " + "取款金额" + m);
money = money - m;
System.out.println(name + "操作,取款后的余额:" + money);
} if(name.equals("支付宝")){
try {
a.notify();//唤醒当前优先级最高的线程,进入就绪状态
// a.notifyAll();//唤醒当前所有的线程,进入就绪状态
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
} class User implements Runnable{
Acount acount;
int money;
public User(Acount acount,int money){
this.acount = acount;
this.money = money;
}
@Override
public void run() {
// acount.drawing(money);
// if(Thread.currentThread().getName().equals("微信")){
//// acount.drawing(money);
// acount.drawing3(money);
// }else{
//// acount.drawing1(money);
// acount.drawing4(money);
// }
// acount.drawing2(money);//调用类的静态方法 // acount.drawing3(money); acount.drawing5(money, acount);
} }

经典生产者消费者问题

package day15;

/**
* 生产者与消费者
*
* @author lby
*
*/
public class Test3 {
public static void main(String[] args) {
Clerk c = new Clerk();
// 消费时不生产,生产时不消费 // 生产者
new Thread(new Runnable() {
@Override
public void run() {
synchronized (c) {
while (true) {// 无限循环代表无限的生产次数
if (c.productNum == 0) {// 产品数为0,开始生产
System.out.println("产品数为0,开始生产");
while (c.productNum < 4) {
c.productNum++;// 增加产品
System.out.println("库存:" + c.productNum);
}
System.out.println("产品数为" + c.productNum + ",结束生产"); c.notify();// 唤醒消费者线程
} else {
try {
c.wait();// 生产者线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}, "生产者").start(); // 消费者
new Thread(new Runnable() {
@Override
public void run() {
synchronized (c) {
while (true) {// 无限循环代表无限的消费次数
if (c.productNum == 4) {// 产品数为4,开始消费
System.out.println("产品数为4,开始消费");
while (c.productNum > 0) {
c.productNum--;// 消费产品
System.out.println("库存:" + c.productNum);
}
System.out.println("产品数为" + c.productNum + ",结束消费"); c.notify();// 唤醒生产者线程
} else {
try {
c.wait();// 消费者线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}, "消费者").start(); }
} class Clerk {
public static int productNum = 0;
}

Java-继承Thread的方式和实现Runnable接口多线程的更多相关文章

  1. Java知多少(58)线程Runnable接口和Thread类详解

    大多数情况,通过实例化一个Thread对象来创建一个线程.Java定义了两种方式: 实现Runnable 接口: 可以继承Thread类. 下面的两小节依次介绍了每一种方式. 实现Runnable接口 ...

  2. 创建线程的一般方式和匿名内部类方式对比——实现runnable接口,重新run方法

    启动:使用静态代理设计模式 优点:可同时实现继承,避免单继承局限性 一般方式: Programer.java /** * 真实角色 * * @author :liuqi * @date :2018-0 ...

  3. Java 继承Thread类和实现Runnable接口的区别

    ava中线程的创建有两种方式: 1.  通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2.  通过实现Runnable接口,实例化Thread类 在实际应用中,我 ...

  4. Thread类的常用方法_sleep和创建多线程程序的第二种方式_实现Runnable接口

    sleep方法是在Thread类中的一个静态方法,当一个线程调用了sleep方法,被调用的那个线程就会暂时的让出指定时间的CPU执行权,在这段时间也不会参与CPU的调度,当时间到了之后,就会重新回到就 ...

  5. Java之多线程方式二(实现Runnable接口)

    /** * 创建多线程的方式二:实现Runnable接口 * 1. 创建一个实现了Runnable接口的类 * 2. 实现类去实现Runnable中的抽象方法:run() * 3. 创建实现类的对象 ...

  6. Android(java)学习笔记66:实现Runnable接口创建线程 和 使用Callable和Future创建线程

    1. 前面说的线程的实现是新写一个子类继承Thread: 是将类声明为 Thread 的子类.该子类应重写 Thread 类的 run 方法.接下来可以分配并启动该子类的实例 2. 这里说的方案2是指 ...

  7. Android(java)学习笔记6:实现Runnable接口创建线程 和 使用Callable和Future创建线程

    1. 前面说的线程的实现是新写一个子类继承Thread: 是将类声明为 Thread 的子类.该子类应重写 Thread 类的 run 方法.接下来可以分配并启动该子类的实例 2. 这里说的方案2是指 ...

  8. 50、多线程创建的三种方式之实现Runnable接口

    实现Runnable接口创建线程 使用Runnable创建线程步骤: package com.sutaoyu.Thread; //1.自定义一个类实现java.lang包下的Runnable接口 cl ...

  9. 【转载】JAVA中线程的两种实现方法-实现Runnable接口和继承Thread类

    转自: http://blog.csdn.net/sunguangran/article/details/6069317 非常感谢原作者,整理的这么详细. 在java中可有两种方式实现多线程,一种是继 ...

  10. 创建线程的两种方式:继承Thread类和实现Runnable接口

    第一种方式:继承Thread类 步骤:1.定义类继承Thread 2.覆写Threa类的run方法. 自定义代码放在run方法中,让线程运行 3.调用线程的star方法, 该线程有两个作用:启动线程, ...

随机推荐

  1. margin-bottom:-1px无效的问题

    在实现tab的时候,margin-bottom:-1px无效的问题 active的tab项,要指定他的border-top, 如: border-top: 1px solid #fff; border ...

  2. JDK源码阅读-------自学笔记(十五)(java.lang.Math数学类)

    Math类简介 用于常见的数学方法 如果需要更加强大的数学运算能力,计算高等数学中的相关内容,可以使用apache commons下面的Math类库 常用方法及实战 abs 绝对值 实例: 1 //绝 ...

  3. 一文了解npm install -g和npm install --save-dev的关系

    本文分享自华为云社区<npm install -g 和 npm install --save-dev 的关系>,作者: SHQ5785. 一.npm install 本地安装 将安装包放在 ...

  4. JavaSE数组

    目录 数组 概念 如何创建数组 数组的访问与迭代 二维数组 定义: 数组的声明 数组创建(会自动进行初始换为0) 数组遍历 数组 概念 ​ 在Java中,数组是一种用于存储多个相同类型元素的数据结构. ...

  5. kettle从入门到精通 第六十四课 ETL之kettle kettle中执行SQL脚本步骤,使用需当心

    1.群里有不定时会有同学反馈执行SQL脚本步骤使用有问题,那么咱们今天一起来学习下该步骤.trans中的执行SQL脚本有两方面功能,使用时需小心,不然很容易踩坑. 官方定义: 翻译: 您可以使用此步骤 ...

  6. SpringBoot系列(一)简介。

    概述: Spring Boot 可以简化spring的开发,可以快速创建独立的.产品级的应用程序. 特征: 快速创建独立的 Spring 应用程序 直接嵌入了Tomcat.Jetty或Undertow ...

  7. 任意树遍历,可以使用 goto 跳记号标注的

    先顺序进入到最后一个根的根部,完后扫描同级 同级扫描完用 goto跳代码改层数到倒数地二层 之后操作就是倒着往上搜索的,有难度,但是还是能做到的嘛 用 lisit 好像不需要别的,全用 list 连接 ...

  8. OAuth + Security - 7 - 异常翻译

    认证异常翻译 默认情况下,当我们在获取令牌时输入错误的用户名或密码,系统返回如下格式响应: { "error": "invalid_grant", " ...

  9. javascript 类class设置访问器setter时出现Maximum call stack size exceeded错误

    Maximum call stack size exceeded这个错误的意思是调用栈溢出,但是自己写的代码基本不可能出现.所以可能的原因是A调用了B,然后B再调用A,形成了循环调用.或者说是A自己调 ...

  10. leetcode | 107. 二叉树的层序遍历 II | javascript实现 | c++实现

    题目 给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 . (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 思路 题目的要求相当于是求层序遍历数组的转置,我们只需利用js的 ...