day17_多线程基础

今日内容

零、 复习昨日

一、作业

二、进程与线程

三、创建线程

四、线程的API

一、复习

IO流的分类

  • 方向: 输入,输出
  • 类型: 字节(XxxStream),字符(XxxReader,XxxWriter)
  • 字节输入流类名: FileInputStream
  • 字节输出流类名: FileOutputStream
  • 字符输入流类名: FileReader
  • 字符输出流类名: FileWriter

利用try-catch-finally代码写法是重点

ArrayList和LinkedList的异同点

  • ArrayList 底层是数组,容量10,装满扩容1.5倍
  • LinkedList 底层是链表
  • 相同点: 都是存储多个元素,有序,且允许重复元素
  • 不同点: 底层实现原理不一样,AL查询更新快 LL插入删除较快

解析字符串日期为日期对象的方法签名

Date date = sdf.parse("2020-01-01");

Date parse(String s)

解析字符串数字为int数字的方法签名

static int parseInt(String s)

int n = Integer.parseInt("1");


String,Date,try-catch-finally

ArrayList,HashMap

认真,慢一点,写一遍(用法,解释,代码)到本上

二、进程与线程[了解]

进程:

一个进程就是一个应用程序,进程包含线程

一个进程至少包含一个线程,大部分都是有多条线程在执行任务(多线程),每个独立运行的程序都对应一个进程。进程是资源分配的最小单位,占用独立的内存空间和系统资源

  • qq,微信,迅雷
    线程:

线程是进程内部的一个执行任务

进程内多个线程是共享进程资源,线程是资源调度的最小单位(CPU调度和分派的基本单位)

  • qq在聊天,视频,传输文件
  • 迅雷同时下载多个资源
    Java程序是否是多线程的吗?

答: 是! main线程,gc垃圾回收线程
为什么需要多线程?

  • 并行处理任务,提高效率
    Java程序本身,是否多线程?

  • 是,至少有一个线程是"main"线程

  • 还有一个线程是没有直接看到,但是一直在"背后"运行,垃圾回收线程(GC)

三、创建线程[重点]

Thread

创建线程的方式有很多

  • 继承Thread
  • 实现Runnable接口
  • 使用Callable 和FutureTask来完成
  • 使用线程池获得线程

3.1 继承Thread

步骤

  1. 自定义类
  2. 继承Thread
  3. 重写run方法
  4. 创建子类对象
  5. 调用start方法启动线程 [特别强调,开启新线程的方法是start ]
    start方法内部执行时会调用run方法执行
java 复制代码
public class MyThread extends Thread{

    // 重写方法,方法内部,就是该线程执行的任务
    @Override
    public void run() {
        for (int i = 1; i < 101; i++) {
            System.out.println("MyThread --> "+i );
        }
    }
}
java 复制代码
public class TestThread{
	public static void main(String[] args) {
        // 创建一个线程
        MyThread myThread = new MyThread( );
        // 启动线程,不是调用run()方法!!!!
        // 调用start方法开启新线程
        myThread.start();

        // ==============main线程执行====================
        for (int i = 1; i < 101; i++) {
            System.out.println("main     --> " + i );
        }
        
        // 自定义线程和主线程同时执行,并且出现资源争抢情况
    }
}

3.2 实现Runnable接口

步骤

  1. 自定义类
  2. 实现Runnable接口
  3. 重写run方法
  4. 创建子实现类对象
  5. 把子实现类对象当构造方法的方法参数来创建Thread对象
  6. 由thread调用start方法开启线程
java 复制代码
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i < 101; i++) {
            System.out.println("MyRunnable --> " + i);
        }
    }
}
java 复制代码
    public static void main(String[] args) {
        // 4 创建实现类对象
        MyRunnable myRunnable = new MyRunnable( );

        // 5 把对象当构造方法参数,创建Thread对象
        Thread thread = new Thread(myRunnable);
        // 6 开启线程
        thread.start();
    }
        

匿名内部类的形式创建线程

java 复制代码
// ============= 匿名内部类完成实现Runnable接口 ============
public class Demo4Annoy {

    public static void main(String[] args) {
        Runnable runnable = new Runnable( ) {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("匿名内部类 --> " + i);
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();

        // new Thread( new Runnable() {
        //     @Override
        //     public void run() {
        //         for (int i = 0; i < 100; i++) {
        //             System.out.println("匿名内部类 --> " + i );
        //         }
        //     }
        // }).start();

        for (int i = 0; i < 100; i++) {
            System.out.println("main --> " + i);
        }
    }
}

3.3 区别

继承Thread开启线程和实现Runnable接口开启线程有什么区别?


  • 一个继承,一个实现
  • 继承Thread后,直接创建对象即可调用start开启线程
  • 实现Runnable接口的子类,还需要再创建Thread类对象才可以调用strat开启线程

从使用便捷度来说,继承Thread开启线程会方便一点...因为创建完线程对象可以直接调用start开启线程

继承Thread类就限制了该类不能再继承别的类,因为类只能单继承,而实现接口的同时可以继承别的类,并且还允许多继承,所以推荐使用接口来实现多线程.

3.4 其他创建线程方式

java 复制代码
package com.qf.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;

public class Callable01 {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		// FutureTask对象用于接收线程返回值, FutureTask 实现了RunnableFuture接口
		// public class FutureTask<V> implements RunnableFuture<V> {}
		// RunnableFuture接口继承了Runnable和Future接口,后面用到的get方法来自Future接口
		// public interface RunnableFuture<V> extends Runnable, Future<V>{}
		FutureTask<String> futureTask = new FutureTask<String>(new MyCallable());
		
		Thread thread = new Thread(futureTask);
		thread.start();

		System.out.println("-----------------");
		// 该语句阻塞程序的执行,等待线程执行完毕,获取线程返回的数据
		String result = futureTask.get();
		System.out.println(result);
	}
}

// 实现Callable接口的类
class MyCallable implements Callable<String> {
	@Override
	public String call() throws Exception {
		System.out.println(Thread.currentThread().getName());
		Thread.sleep(5000);
		return "haha";
	}
}

四、Thread的API[熟悉]

  • void start() 开启线程,会自动run方法执行线程任务

  • void run() 执行线程的方法

    • run() 方法是start开启线程后,JVM自动调用
  • void setName(String name) 给线程设置名字

    • 也可以通过构造方法,在创建线程时指定线程名
  • String getName() 获得线程的名字

  • static Thread currentThread() 返回当前正在执行的线程对象

  • join() 加入线程,等待该线程终止

  • join(int milles) 加入线程,最大等待直到毫秒数

  • void setDaemon(boolean on) 设置守护线程

  • static void sleep(long milles) 线程休眠

    • 会让当前线程暂停执行,让出系统资源,让其他线程执行
    • 时间到,当前会继续和其他争抢资源执行
  • void stop() 结束当前线程,线程死亡(已过式)

    • 被废弃的原因是因为这个中断线程太直接太暴力...
      线程名字
  • String getName()

  • void setName()

  • Thread(String name)

java 复制代码
package com.qf.thread_api;

/**
 * --- 天道酬勤 ---
 *
 * @author QiuShiju
 * @desc 关于线程名字操作
 */
public class Demo1 {

    public static void main(String[] args) {

        new Thread(){
            @Override
            public void run() {
                // 给线程设置名字
                this.setName("驴车");
                // 创建线程,默认会分配名字,安装thread-x来命名
                System.out.println("开启新线程,线程名: " + getName());
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                this.setName("火车");
                // 创建线程,默认会分配名字,安装thread-x来命名
                System.out.println("开启新线程,线程名: " + getName());
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                this.setName("飞机");
                // 创建线程,默认会分配名字,安装thread-x来命名
                System.out.println("开启新线程,线程名: " + getName());
            }
        }.start();

        Thread thread = new Thread(){
            @Override
            public void run() {
            }
        };
        System.out.println(thread.getName( ));
        thread.setName("火箭");
        System.out.println(thread.getName( ));

        /**
         * 还可以通过构造方法,在创建线程时指定线程名
         */
        Thread thread5 = new Thread("子弹头"){
            @Override
            public void run() {
            }
        };
        System.out.println("线程5-->" + thread5.getName( ));
    }
}

获得当前线程对象

  • static Thread currentThread()
java 复制代码
public static void main(String[] args) {

    new Thread("火车"){
        @Override
        public void run() {
            Thread t = Thread.currentThread( );
            System.out.println(t.getName()+",在执行..." );
        }
    }.start();

    /**
     * 获得当前正在执行的线程对象
     */
    Thread thread = Thread.currentThread( );
    System.out.println("当前正在执行的线程名字是: "+thread.getName( ));
}

线程加入

  • join()
  • join(long mill)
java 复制代码
public static void main(String[] args) {

    Thread t1 = new Thread("线程1"){
        @Override
        public void run() {
            for (int i = 1; i < 1001; i++) {
                System.out.println(getName()+"-->"+i );
            }
        }
    };

    Thread t2 = new Thread("线程2"){
        @Override
        public void run() {
            for (int i = 1; i < 1001; i++) {
                System.out.println(getName()+"-->"+i );
                if (i == 100) {
                    try {
                        // 在当前线程下加入另一个线程
                        // 相当于"插队",直到插入的线程执行完
                        //t1.join();
                        // 让另外一个线程加入指定时间,到时后继续争抢
                        t1.join(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace( );
                    }
                }
            }
        }
    };

    t1.start();
    t2.start();
}

守护线程

  • void setDaemon(boolean x)

  • 守护线程是指,A线程守护B线程,即B是被保护,B是重要的,当B线程结束时A线程(守护线程)会立即结束

java 复制代码
public static void main(String[] args) {
    Thread hs = new Thread("皇上"){
        @Override
        public void run() {
            for (int i = 1; i < 11; i++) {
                System.out.println(getName()+"工作"+i+"天" );
            }
        }
    };

    Thread fz = new Thread("妃子"){
        @Override
        public void run() {
            for (int i = 1; i < 101; i++) {
                System.out.println(getName()+"工作"+i+"天" );
            }
        }
    };

    /**
     * 不需要设置被守护,只需要设置守护线程,其他默认就是被守护线程
     * 开启线程前设置
     */
    fz.setDaemon(true);

    hs.start();
    fz.start();
}

线程休眠

  • sleep(long millis)
  • 会让当前线程陷入等待状态(阻塞)状态,让其资源让其他线程执行
java 复制代码
public static void main(String[] args) throws InterruptedException {

    for (int i = 10; i > 0; i--) {
        System.out.println("倒计时 --> " + i );
        Thread.sleep(1000);
    }
    System.out.println("发射!" );
}

private static void show() {
    Thread t1 = new Thread("线程1"){
        @Override
        public void run() {
            for (int i = 1; i < 101; i++) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace( );
                }
                System.out.println(getName()+"-->"+i );
            }
        }
    };

    Thread t2 = new Thread("线程2"){
        @Override
        public void run() {
            for (int i = 1; i < 101; i++) {
                System.out.println(getName()+"-->"+i );
            }
        }
    };

    t1.start();
    t2.start();
}

结束线程

  • void stop() 结束当前线程
  • void interrupt() 设置线程中断状态为以中断
  • boolean isInterrupt() 测试返回线程是否中断
java 复制代码
public static void main(String[] args) {
    new Thread("线程2"){
        @Override
        public void run() {
            for (int i = 1; i < 101; i++) {
                if (i == 15){
                    //this.stop(); // 直接结束线程
                    this.interrupt();// 给线程设置一个中断状态,不会真的中断线程
                    // 结束不结束取决于线程本身执行情况
                }
                System.out.println("是否中断? "+this.isInterrupted() );
                System.out.println(getName()+"-->"+i );
            }
        }
    }.start();
}

五、线程状态[面试]

线程的几种状态:

  • 创建/新建/初始
    • new 完线程对象
  • 就绪
    • 调用start
  • 等待/阻塞
    • join()或者sleep()
  • 运行
    • 执行run()方法
  • 死亡/销毁/终止
    • run方法执行完,或者调用stop()或者interrupt()中断等

清楚状态之间的转换

相关推荐
Chrikk21 分钟前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*24 分钟前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue25 分钟前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man27 分钟前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟28 分钟前
使用python向钉钉群聊发送消息
java·python·钉钉
P.H. Infinity1 小时前
【RabbitMQ】04-发送者可靠性
java·rabbitmq·java-rabbitmq
生命几十年3万天1 小时前
java的threadlocal为何内存泄漏
java
caridle2 小时前
教程:使用 InterBase Express 访问数据库(五):TIBTransaction
java·数据库·express
萧鼎2 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸2 小时前
【一些关于Python的信息和帮助】
开发语言·python