首页 > 编程语言 >java多线程基础

java多线程基础

时间:2023-01-16 01:56:01浏览次数:62  
标签:java Thread void 基础 线程 println new 多线程 public

java多线程编程

课程视频来源:多线程06:初识并发问题_哔哩哔哩_bilibili

线程简介

进程

编写的静态代码文件,经过编译成为可执行的二进制文件,执行后它被加载至内存,被CPU执行。这个运行的程序就是进程

线程

为何要引入线程

如视频播放时,需要三个核心模块:1. 从视频读取数据,2. 数据解压缩,3. 解压缩的数据播放。单个进程肯定是按顺序执行这3个模块,但造成的问题是画面与声音不连贯,函数之间无法并发执行,效率低。那改成多进程呢?问题依旧存在:不同进程通信不方便,维护进程的开销大。

所以需要一种新的实体,满足以下两点,它便是线程。

  1. 实体间可并发执行
  2. 共享相同的地址空间。

什么是线程

线程thread是操作系统调度的最小单位,多数情况下,线程被包含与进程process之中,是其实际运行单位。一个进程可并发多个线程,各个线程执行不同的任务。每个线程有自己独立的寄存器和栈,确保各个线程独立。

java中线程的3中创建方式

  1. 继承Thread
  2. 实现Runnable接口
  3. 实现Callable接口

继承Thread类

创建一个子线程类Mythead继承Thead类,重写run方法即可

package ThreadExtend;

public class MyThread extends Thread {
    @Override
    public void run() {
       for(int i = 0;i < 10;++i){
           System.out.println("son: "+ Thread.currentThread());
       }
    }
}

主线程调用

import ThreadExtend.MyThread;

public class Main {
      public static void main(String[] args) {
        test1();
        for (int i = 0; i < 30; ++i) {
            System.out.println("father: " + Thread.currentThread());
        }
    }

   public static void test1()
    {
        MyThread t = new MyThread();
        t.start();
    }
}


注意:

  1. 只有通过start调用才会开启子线程,调用run只是普通的调用。
  2. 主线程中只有开启线程的之后的代码,才会和子线程并发执行。

实现Runnable接口

实现Runnable接口,重写run方法。

package RunnableInterface;

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i< 20;++i){
            System.out.println("Runnable实现线程:"+Thread.currentThread());
        }
    }
}

创建线程:

 public static void test2(){
        MyRunnable t = new MyRunnable();
        new Thread(t).start();
}

查看Thread.java原码文件可发现,实际上Thread类正是实现了Runnable接口:

public class Thread implements Runnable
图片名称

推荐使用Runnable接口!!!

下面展示同一个对象被多个线程访问的例子

package RunnableInterface;

public class TestRunnable implements Runnable {
    private  int totalTicketNum;
   // private String name;
    TestRunnable(int num){
        this.totalTicketNum = num;
    }

    @Override
    public  void run() {
        buy();
    }

     void buy(){
        try {
            while(true){
                synchronized (this) {
                    if(totalTicketNum > 0){
                        System.out.println(Thread.currentThread().getName() + "拿到了第" + totalTicketNum-- + "票");
                    }
                    else{
                        break;
                    }
                }
                Thread.sleep(300);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

package RunnableInterface;

public class Main {
    public static void main(String[] args) {
        TestRunnable t = new TestRunnable(10);
        new Thread(t,"小明").start();
        new Thread(t,"小花").start();
        new Thread(t,"小三").start();

    }
}

问题:为啥不把buy整体同步呢?因为想让totalTicketNum 变为0的时候线程结束执行,如果synchronized 同步整个buy方法,里面的while的存在导致totalTicketNum>0时,第一个线程一直持有锁,其他线程无法进入,导致只有一个人拿到了所有票。同时为了避免因为线程执行过快让一个人拿了全部票,添加了延时。

Callable接口

特点:

  1. 实现Callable接口,需要返回值类型
  2. 重写call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务 ExecutorService ser = Executors.newFixedThreadPool(int nThreads);
  5. 提交执行​ Futureinteger ft1 = ser.submit(t1);
  6. 获取结果 ​ ft1.get()
  7. 关闭服务ser.shutdownNow();

例子:

package CallableInterface;

import java.util.concurrent.Callable;

public class MyCall implements Callable<Integer> {
    private int n;
    public MyCall(int n){
        this.n = n;
    }
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for(int i = 1;i <= n;++i){
            sum += i;
        }
        System.out.println(Thread.currentThread().getName() + "计算1 + 2 + ... + " + n + " = " + sum);
        return sum;
    }
}

package CallableInterface;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCall t1 = new MyCall(100);
        MyCall t2 = new MyCall(200);
        MyCall t3 = new MyCall(300);
        MyCall t4 = new MyCall(400);

        ExecutorService ser = Executors.newFixedThreadPool(3);
        Future<Integer> ft1 = ser.submit(t1);
        Future<Integer> ft2 = ser.submit(t2);
        Future<Integer> ft3 = ser.submit(t3);
        Future<Integer> ft4 = ser.submit(t4);

        System.out.println("返回值1:" + ft1.get());
        System.out.println("返回值2:" + ft2.get());
        System.out.println("返回值3:" + ft3.get());
        System.out.println("返回值4:" + ft4.get());

        ser.shutdownNow();
    }
}

执行结果:

图片名称

https://s2.loli.net/2022/09/26/w3RAbNcMIu1WKTo.png

图片名称

​​

lambda表达式

为何要lambda表达式?

避免匿名内部类定义过多,关注核心逻辑,让代码更简洁。

应用场景

任何接口interface,若只包含一个抽象方法,那么可以i通过lambda表达式创建改接口对象。

特点

  • 可省略参数类型
  • 一个参数可省略括号
  • 一条语句可省略花括号
package Lambda;

public class MyLambda {
    static class Like2 implements ILike{
        @Override
        public void say() {
            System.out.println("peipei like me");
        }
    }

    public static void main(String[] args) {
         ILike like = new Like1();

         like.say();

         like = new Like2();
         like.say();
        //局部内部类
        class Like3 implements ILike{
            @Override
            public void say() {
                System.out.println("Marry me, peipei!");
            }
        }
        like = new Like3();
        like.say();

        like = new ILike() {
            @Override
            public void say() {
                System.out.println("I will marry peipei");
            }
        };
        like.say();
        like = ( )->{
                System.out.println("peipei will marry me!");
        };

        like.say();
    }
}

//函数接口
interface ILike{
    void say();
}

class Like1 implements ILike{
    @Override
    public void say() {
        System.out.println("I love peipei");
    }
}

线程状态

线程5个状态

图片名称

线程方法

图片名称

终止线程

一般用标志法,即外部修改变量启动或者停止线程

package ThreadExtend;

public class MyThread extends Thread {
    volatile private Boolean flag = true;
    @Override
    public void run() {
        long num = 0;
       while(flag){
           try {
               Thread.sleep(50);
           } catch (InterruptedException e) {
               throw new RuntimeException(e);
           }
           num++;
       }
        System.out.println("num = " + num);
    }
    public void stopThread(){
        flag = false;
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread t = new MyThread();
        t.start();
        for(int i = 0;i<100;++i){
           if(i < 50){
                Thread.sleep(10);
            }
           else if(i == 50){
                System.out.println("线程停止");
                t.stopThread();
               break;
            }
        }
        t.join();
    }
}

操作系统往往将变量缓存下来,故用volatile 修饰,使得读取flag时强制从其内存地址读取最新值。

线程休眠sleep

sleep(int millisecond); 时间达到后线程进入就绪状态,可用于模拟网络延时,倒计时等。每个对象都有一把锁,sleep不会释放锁.

线程礼让yield

礼让的线程先出来,再和其他线程竞争。线程礼让一定成功,但CPU依旧可能会调度它。

package RunnableInterface;


public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始");
        System.out.println(Thread.currentThread().getName()+"结束");
    }

}

package RunnableInterface;

public class MyYeildRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"结束");
    }
}


package RunnableInterface;

public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        MyYeildRunnable myYeildRunnable = new MyYeildRunnable();
        new Thread(myYeildRunnable,"a").start();
        new Thread(myRunnable,"b").start();

    }
}

/*礼让成功
a开始
b开始
b结束
a结束
*/

join线程

合并线程,听不懂???就是插队执行,其他线程阻塞等我执行完你再接着。

package ThreadExtend;

public class MyThread extends Thread {

    @Override
    public void run() {
       for(int i = 0; i< 1000;++i){
           System.out.println("vip来了!" + i);
       }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread t = new MyThread();
        t.start();
        for(int i = 0;i<100;++i){
            if(i == 50){
                System.out.println("aaa:" + t.getState());
                t.join();
                System.out.println("bbb:" + t.getState());
            }
            System.out.println("main" + i);
        }

    }
}

图片名称 图片名称

线程优先级

事实上没什么卵用

public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();

        Thread t1 = new Thread(myRunnable,"a");
        Thread t2 = new Thread(myRunnable,"b");
        Thread t3 = new Thread(myRunnable,"c");
        Thread t4 = new Thread(myRunnable,"d");

        t1.setPriority(4);
        t2.setPriority(8);
        t3.setPriority(2);
        t4.setPriority(10);

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

守护线程

什么是守护线程?

java中线程分为:1. 用户线程, 2. 守护线程。 守护线程通常后台执行,为用户线程服务。JVM的垃圾回收线程是典型的守护线程,在程序执行时它一直保持执行,但当所有线程结束,它就可以结束了。

package RunnableInterface;


class DaemonThred  implements Runnable{
    @Override
    public void run() {
        int i = 0;
        while(true){
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("人生路漫漫,活着的第" + i++ +"天");
        }
    }
}

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i  = 1;i <= 365;++i){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("今天是一年中第" +i + "天");
        }
    }
}


package RunnableInterface;

public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        DaemonThred daemonThred = new DaemonThred();

        Thread t =  new Thread(daemonThred);
        t.setDaemon(true);   //设置为守护线程
        t.start();
        new Thread(myRunnable).start();
    }
}

图片名称

标签:java,Thread,void,基础,线程,println,new,多线程,public
From: https://www.cnblogs.com/shmilyt/p/17054569.html

相关文章

  • C++11的多线程开发
    多线程并发总览创建线程使用互斥量异步线程原子类型生产者消费者模型线程池‍创建线程#include<iostream>#include<thread>#include<mutex>#include<fu......
  • Matlab笔记--Matlab基础
    Matlab基础数据类型(共有15种数据类型)整数取整函数浮点数(单精度浮点数和双精度浮点数--默认为双精度浮点数)复数数据的显示格式(format确定数据的显示格式):数据格式......
  • 【博学谷学习记录】超强总结,用心分享 | pyspark基础操作
    【博学谷IT技术支持】Spark是一种快速、通用、可扩展的大数据分析引擎,2009年诞生,2010年开源,2013年成为Apache孵化项目,2014年成为Apache顶级项目。目前,Spark生态系统已经发......
  • JavaScript学习笔记—window对象
    window对象浏览器为我们提供了一个window对象,可以直接访问window对象代表的是浏览器窗口,通过该对象可以对浏览器窗口进行各种操作,除此之外window对象还负责存储JS中的内......
  • JavaScript学习笔记
     JavaScript学习笔记概念脚本语言:一行一行的翻译成机器语言,并一行一行的执行;而不是全部编译执行; 作用表单动态校验、网页特效、服务端开发Node.js、桌......
  • Python爬虫-第四章-1-多线程多进程并发爬取Ⅰ
    多线程多进程用的方式#DemoDescribe:多线程写法fromthreadingimportThread#线程frommultiprocessingimportProcess#进程#start--------1,多线程调用的方式一......
  • 前端的基础知识
    网页的相关概念什么是网页网站:是指在因特网上根据一定规则,使用HTML等制作的用于展示特定内容相关的网页集合。网页:网页通常是HTML格式的文件(是常以.htm或.html为后缀......
  • Python爬虫-第四章-1-多线程多进程并发爬取Ⅱ
    线程池使用案例:一次性开辟一些线程,用户直接给线程池提交任务,线程任务的调度交给线程池#DemoDescribe:线程池和进程池fromconcurrent.futuresimportThreadPoolExecutor,......
  • C 语言局部 static 变量多线程 DataRace 验证
    验证局部静态变量staticintcnt在无锁情况下的datarace:测试C源码:#include<stdio.h>#include<pthread.h>#include<stdlib.h>void*foo(void*args){s......
  • Python爬虫-第四章-1-多线程多进程并发爬取Ⅲ-爬取某菜市场批发价数据
    #DemoDescribe:爬取北京新发菜地数据importrequestsimportcsvfromconcurrent.futuresimportThreadPoolExecutorfromfake_useragentimportUserAgent'''本章内容:......