java学习多线程:线程创建线程状态线程同步线程通信全总结(代码片段)

毛_三月 毛_三月     2023-02-02     560

关键词:

1、基本概念

  • 进程

    • 在操作系统中运行的程序就是进程,进程就是执行程序的一次执行过程,它是一个动态的概念式系统资源分配的单位
    • 通常再一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义,线程是CPU调度和执行的单位
  • 线程

    • 线程就是独立的执行路径
    • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,比如主线程,GC线程
    • main()称之为主线程,为系统的入口,用于执行整个程序
    • 在一个进程中,如果开辟了多个线程,线程的运行是由调度器安排调度的,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
    • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
    • 线程会带来额外的开销,如CPU调度时间,并发控制开销
    • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
  • 多线程

    • 多条执行路径,主线程与子线程并行交替执行(普通方法只有主线程一条路径)

2、线程创建

2.1、 继承 Thread 类(重点)

Thread API

Class Thread


public class Thread
extends Object
implements Runnable

线程是程序中执行的线程。Java虚拟机允许应用程序同时执行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。
当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某些指定类的名为main的方法)。 Java虚拟机将继续执行线程,直到发生以下任一情况:

    • 已经调用了Runtime类的exit方法,并且安全管理器已经允许进行退出操作。
    • 所有不是守护进程线程的线程都已经死亡,无论是从调用返回到run方法还是抛出超出run方法的run

创建一个新的执行线程有两种方法。 一个是将一个类声明为Thread的子类。 这个子类应该重写run类的方法Thread 。 然后可以分配并启动子类的实例。 例如,计算大于规定值的素数的线程可以写成如下:


     class PrimeThread extends Thread 
         long minPrime;
         PrimeThread(long minPrime) 
             this.minPrime = minPrime;
         

         public void run() 
             // compute primes larger than minPrime
              . . .
         
     

然后,以下代码将创建一个线程并启动它运行:

     PrimeThread p = new PrimeThread(143);
     p.start();

另一种方法来创建一个线程是声明实现类接口。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动。 这种其他风格的同一个例子如下所示:


     class PrimeRun implements Runnable 
         long minPrime;
         PrimeRun(long minPrime) 
             this.minPrime = minPrime;
         

         public void run() 
             // compute primes larger than minPrime
              . . .
         
     

然后,以下代码将创建一个线程并启动它运行:

     PrimeRun p = new PrimeRun(143);
     new Thread(p).start();

每个线程都有一个用于识别目的的名称。 多个线程可能具有相同的名称。 如果在创建线程时未指定名称,则会为其生成一个新名称。
除非另有说明,否则将参数传递给null中的构造函数或方法将导致抛出NullPointerException

    • 从以下版本开始:
      JDK1.0
    • 另请参见:
      RunnableRuntime.exit(int)run()stop()
  1. 自定义线程类,继承Thread类

  2. 重写run()方法,编写线程执行体

  3. 在主函数中创建一个线程对象,调用start()方法开启线程。

案例:

案例1:主线程调用run

package com.mao.demo01;

/**
 * @ClassName TestThread1
 * @Description TODO
 * @Author Huchao
 * @Date 2021/11/7 16:21
 * @Version 1.0
 **/
public class TestThread1 extends Thread 
    @Override
    public void run() 
       // run 方法体线程
        for (int i = 0; i < 10; i++) 
            System.out.println("我在看代码----" + i);
        
    

    public static void main(String[] args) 

        // 创建一个线程
        final TestThread1 testThread1 = new TestThread1();

        // start 开启线程
//        testThread1.start();
        testThread1.run();

        // 主线程
        for (int i = 0; i < 200; i++) 
            System.out.println("我在学习多线程----"+i);
        
    

案例2:主线程调用start

package com.mao.demo01;

/**
 * @ClassName TestThread1
 * @Description TODO
 * @Author Huchao
 * @Date 2021/11/7 16:21
 * @Version 1.0
 **/
public class TestThread1 extends Thread 
    @Override
    public void run() 
       // run 方法体线程
        for (int i = 0; i < 10; i++) 
            System.out.println("我在看代码----" + i);
        
    

    public static void main(String[] args) 

        // 创建一个线程
        final TestThread1 testThread1 = new TestThread1();

        // start 开启线程
        testThread1.start();
//        testThread1.run();

        // 主线程
        for (int i = 0; i < 200; i++) 
            System.out.println("我在学习多线程----"+i);
        
    

总结:线程开启不一定立即执行,由CPU调度执行。

案例:图片下载

package com.mao.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * @ClassName TestThread2
 * @Description TODO
 * @Author Huchao
 * @Date 2021/11/8 19:32
 * @Version 1.0
 **/
public class TestThread2  extends Thread
    private String url;     // 网络图片地址
    private String name;    // 保存的文件名

    public TestThread2(String url, String name) 
        this.url = url;
        this.name = name;
      

    // 下载图片的线程执行体
    @Override
    public void run() 
//        super.run();
        final webDownloader webDownloader = new webDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
    

    public static void main(String[] args) 
        final TestThread2 test1 = new TestThread2("https://i0.hdslb.com/bfs/article/f39427eefe8b5c9318b6c9ef99c4108efdf1e747.jpg@1320w_740h.webp","edg1.png");
        final TestThread2 test2 = new TestThread2("https://i0.hdslb.com/bfs/archive/57ab5a6e8b36c227cc13c2cd96270e04b0a253be.png","edg2.png");
        final TestThread2 test3 = new TestThread2("https://i0.hdslb.com/bfs/activity-plat/static/b711c4ecbed559f94155437efb3d8532/j6mFBYivhq_w1920_h658.png","文豪试炼场.png");

        // 实际下载并不是按照顺序执行
        test1.start();
        test2.start();
        test3.start();
    


// 下载器
class webDownloader
    // 下载方法
    public void downloader(String url,String name)

        try 
            FileUtils.copyURLToFile(new URL(url),new File(name));   // 讲url变成图片
         catch (IOException e) 
            e.printStackTrace();
            System.out.println("IO 异常,downloader方法出现问题");
        
    


实际下载并未按照顺序执行

2.2、 实现Runnable接口(重点)

Runnable接口API

另一种方法来创建一个线程是声明实现类接口。 那个类然后实现了run方法。 然后可以分配类的实例,在创建Thread时作为参数传递,并启动。 这种其他风格的同一个例子如下所示:


     class PrimeRun implements Runnable 
         long minPrime;
         PrimeRun(long minPrime) 
             this.minPrime = minPrime;
         

         public void run() 
             // compute primes larger than minPrime
              . . .
         
     

然后,以下代码将创建一个线程并启动它运行:

     PrimeRun p = new PrimeRun(143);
     new Thread(p).start();

每个线程都有一个用于识别目的的名称。 多个线程可能具有相同的名称。 如果在创建线程时未指定名称,则会为其生成一个新名称。
除非另有说明,否则将参数传递给null中的构造函数或方法将导致抛出NullPointerException

    • 从以下版本开始:
      JDK1.0
    • 另请参见:
      RunnableRuntime.exit(int)run()stop()
  1. 自定义线程类,实现Runnable接口

  2. 重写run()方法,编写线程执行体

  3. 执行线程需要丢入runnable接口实现类,调用start()方法。

案例:

package com.nty.test02;

public class TestThread2 implements Runnable 
    @Override
    public void run() 
        //run方法线程方法体
        for (int i = 0; i < 20; i++) 
            System.out.println("我在看代码----" + i);
        
    

    public static void main(String[] args) 

        //创建一个线程对象
        TestThread2 testThread2 = new TestThread2();

        //创建线程对象,通过线程对象来开启线程,代理
//        Thread thread = new Thread(testThread2);
//
//        //start开启线程
//        thread.start();
        new Thread(testThread2).start();

        //主线程
        for (int i = 0; i < 200; i++) 
            System.out.println("我在学习多线程-----" + i);
        
    

以上两种方式的比较:

继承 Thread 类

  • 子类继承 Thread 类具备多线程能力

  • 启动线程:子类对象 .start()

  • 不建议使用:避免 OOP 单继承局限性

  • 实现 Runnable 接口

// Thread 源码探究
public
class Thread implements Runnable 
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static 
        registerNatives();
    

    private volatile String name;
    private int            priority;
    private Thread         threadQ;
    private long           eetop;

    /* Whether or not to single_step this thread. */
    private boolean     single_step;

实现接口 Runnable

  • 具有多线程能力

  • 启动线程:传入目标对象 + Thread对象.start()

  • 推荐使用:避免单继承局限性,方便同一个对象被多个线程使用。

@FunctionalInterface
public interface Runnable 
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();

初识并发问题——买火车票

package com.mao.demo01;

import com.sun.org.apache.bcel.internal.generic.NEW;

/**
 * @ClassName TestThread
 * @Description TODO
 * @Author HuChao
 * @Date 2021/11/8 20:40
 * @Version 1.0
 **/

// 多个线程同时操作同一个对象
// 买火车票的例子
// 发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class TestThread4 implements Runnable

    // 票数
    private int ticketNums = 10;

    @Override
    public void run() 
        while (true)
            if (ticketNums<=0)
                break;
            
            try 
                Thread.sleep(200);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"票");
        
    

    public static void main(String[] args) 
        final TestThread4 testThread4 = new TestThread4();
        new Thread(testThread4,"超哥").start();
        new Thread(testThread4,"废物凤").start();
        new Thread(testThread4,"黄牛").start();
    

执行结果:

"E:\\Program Files\\Java\\bin\\java.exe" "-javaagent:D:\\Program Files (x86)\\IDEA\\IntelliJ IDEA 2020.3.2\\lib\\idea_rt.jar=60780:D:\\Program Files (x86)\\IDEA\\IntelliJ IDEA 2020.3.2\\bin" -Dfile.encoding=UTF-8 -classpath "E:\\Program Files\\Java\\jre\\lib\\charsets.jar;E:\\Program Files\\Java\\jre\\lib\\deploy.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\access-bridge-64.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\cldrdata.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\dnsns.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\jaccess.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\jfxrt.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\localedata.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\nashorn.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\sunec.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\sunjce_provider.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\sunmscapi.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\sunpkcs11.jar;E:\\Program Files\\Java\\jre\\lib\\ext\\zipfs.jar;E:\\Program Files\\Java\\jre\\lib\\javaws.jar;E:\\Program Files\\Java\\jre\\lib\\jce.jar;E:\\Program Files\\Java\\jre\\lib\\jfr.jar;E:\\Program Files\\Java\\jre\\lib\\jfxswt.jar;E:\\Program Files\\Java\\jre\\lib\\jsse.jar;E:\\Program Files\\Java\\jre\\lib\\management-agent.jar;E:\\Program Files\\Java\\jre\\lib\\plugin.jar;E:\\Program Files\\Java\\jre\\lib\\resources.jar;E:\\Program Files\\Java\\jre\\lib\\rt.jar;E:\\soft\\workspace\\java\\多线程\\out\\production\\多线程;E:\\soft\\workspace\\java\\多线程\\src\\com\\lib\\commons-io-2.11.0.jar" com.mao.demo01.TestThread4
超哥-->拿到了第10票
废物凤-->拿到了第9票
黄牛-->拿到了第8票
废物凤-->拿到了第7票
黄牛-->拿到了第7票
超哥-->拿到了第7票
超哥-->拿到了第6票
黄牛-->拿到了第5票
废物凤-->拿到了第6票
黄牛-->拿到了第4票
超哥-->拿到了第2票
废物凤-->拿到了第3票
黄牛-->拿到了第1票
超哥-->拿到了第-1票
废物凤-->拿到了第0Process finished with exit code 0

发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱

龟兔赛跑问题:

package com.mao.demo01;

/**
 * @ClassName Race
 * @Description TODO
 * @Author HuChao
 * @Date 2021/11/8 21:14
 * @Version 1.0
 **/
public class Race implements Runnable 
    // 胜利者  静态  保证只有一个winner
    private static String winner;
    @Override
    public void run() 

        for (int i = 0; i <= 100; i++) 

            // 模拟兔子休息
            if (Thread.currentThread().getName().equals("兔子")&&i%10==0)
                try 
                    Thread.sleep((long) 0.1);
                 catch (InterruptedException e) 
                    e.printStackTrace();
                
            

            // 判断比赛是否结束
            boolean flag = gameover(i);
            // 如果比赛结束  就停止比赛
            if (flag)
                break;
            

            System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
        

    

    // 判断比赛是否完成
    private boolean gameover(int steps)
        if (winner!=null)  // 已经存在胜利者
            return true;    // 比赛结束
        else 
            if (steps>=100)
                winner=Thread.currentThread().getName();
                System.out.println("winner is"+winner);
                return true;
            
        
        return false;
    

    public static void main(String[] args) 
        final Race race = new Race();
        new Thread(race,"兔子").start()查看详情  

java基础——线程的创建和状态

目录前言创建多线程的方式1继承thread抽象类2实现Runnable接口3实现Callable接口匿名内部类线程池线程安全同步代码块同步方法锁机制线程状态前言进程:内存运行的程序。线程:进程中的一个执行单元。创建多线程的方式本质都... 查看详情

多线程编程学习笔记——线程池

 接上文 多线程编程学习笔记——线程同步(一) 接上文多线程编程学习笔记——线程同步(二) 接上文多线程编程学习笔记——线程同步(三)       创建多线程操作... 查看详情

多线程thread线程创建(代码片段)

...程今日内容介绍?Thread?线程创建?线程安全?线程状态今日学习目标?能够描述Java中多线程运行原理?能够使用继承类的方式创建多线程?能够使用实现接口的方式创建多线程?能够说出实现接口方式的好处?能够解释安全问题的出现的... 查看详情

java线程操作

目录前言创建多线程的方式继承thread抽象类实现Runnable接口匿名内部类线程池线程安全同步代码块同步方法锁机制线程状态前言进程:内存运行的程序。线程:进程中的一个执行单元。创建多线程的方式继承thread抽象类创建publicc... 查看详情

java学习笔记---多线程同步的五种方法

一、引言前几天面试,被大师虐残了,好多基础知识必须得重新拿起来啊。闲话不多说,进入正题。二、为什么要线程同步因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变... 查看详情

多线程编程学习笔记

多线程编程目录线程概述线程的创建创建线程程序线程同步守护线程线程之间的相互通讯线程池和java.util.concurrent包一、概述1.相关概念进程(Process):程序(任务)执行的过程,每个进程都有自己独立的一块内存空间,一个进程中可... 查看详情

尚硅谷_java零基础教程(多线程)--学习笔记(代码片段)

Java多线程一、基本概念1、程序、进程、线程2、单核CPU和多核CPU、并行与并发3、使用多线程的优点二、线程的创建和使用1、API中创建线程的两种方式1.1、方式一:继承Thread类1.2、方式二:实现Runnable接口1.3、Thread类的调... 查看详情

多线程学习-基础线程状态装换

一、线程状态转换下面的这个图非常重要!你如果看懂了这个图,那么对于多线程的理解将会更加深刻  状态说明:(1)新建状态(New):新创建了一个线程对象。(2)就绪状态(Runnable):线程被创建后,其他线程调用了... 查看详情

基础学习day11--多线程一线程的创建,运行,同步和锁

1.1、进程和线程进程:一个应用程序一般都是一个进程,正在进行的程序 每一个进程最少都有一个线程,都有一个执行顺序,该顺序是一个执行路径或者一个控制单元 线程:进程中一个独立的控制单元,线程控制着进程... 查看详情

多线程java多线程学习笔记|多线程基础知识(代码片段)

Java多线程学习笔记|多线程基础知识文章目录Java多线程学习笔记|多线程基础知识一.线程与进程二.Java中创建线程1.编写继承Thread的类,重写run方法2.编写实现Runnable的类,重写run方法3.其他方式的创建三.线程的生命周期四.... 查看详情

java多线程4.构建并发模块

委托是创建线程安全类的一个最有效策略:只需让现有的线程安全类管理所有的状态即可Java类库包含丰富的并发基础构建模块,如线程安全的容器以及各种用于协调多个相互协作的线程控制流的同步工具类1.同步容器类这些类的... 查看详情

多线程(代码片段)

主要内容线程同步线程状态等待与唤醒案例线程池学习目标[]说出进程的概念[]说出线程的概念[]能够理解并发与并行的区别[]能够开启新线程[]能够描述Java中多线程运行原理[]能够使用继承类的方式创建多线程[]能够使用实现接口... 查看详情

java多线程并发02——线程的生命周期与常用方法

线程生命周期一个线程不是被创建了马上就开始执行,也不是一直处于执行状态。在线程的整个生命周期中会经历新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和销毁(Terminated)5种状态。线程全生命周期.PNG新... 查看详情

201771010111李瑞红《第十七周学习总结》(代码片段)

实验十七 线程同步控制实验时间2018-12-10一、理论部分1.Java通过多线程的并发运行提高系统资源利用率,改善系统性能。2.假设有两个或两个以上的线程共享某个对象,每个线程都调用了改变该对象类状态的方法,就会引起的... 查看详情

java线程和多线程——threadlocal

Java中的ThreadLocal是用来创建线程本地变量用的。我们都知道,访问某个对象的所有线程都是能够共享对象的状态的,所以这个对象状态就不是线程安全的。开发者可以通过使用同步来保证线程安全,但是如果不希望使... 查看详情

52java多线程剖析

线程的状态:线程共有下面4种状态:新建状态(New):新创建了一个线程对象,当你用new创建一个线程时,该线程尚未运行。就绪状态(Runnable):线程对象创建后,其他线程调用了该... 查看详情

多线程简介

多线程(1)掌握Executors可以创建的三种线程池的特点及适用范围。1.继承Thread类,重写父类run()方法2.实现runnable接口3.使用ExecutorService、Callable、Future实现有返回结果的多线程(JDK5.0以后) (2)多线程同步机制。在需要同步的方法的... 查看详情