JAVA多线程学习

文章目录

线程相关概念

  • 线程:是进程的一部分,一个进程之内的线程之间共享进程的地址空间、资源。

  • 进程:一个进程可以包含干个线程,但至少有一个线程(线程是CPU调度和执行的单位)。每个进程有独立的地址空间、全局变量、系统资源。是系统资源分配的单位。

  • 多线程:一个进程中并发多个线程,每个线程执行不同任务。

很多多线程是模拟出来的。真正的多线程是指有多个CPU(即多核,多服务)。如果是模拟出来的线程,即在一个cpu的情况下,在同一时间点,cpu只能执行一个代码,因为切换的很快,造成同时执行的错觉。

相关概念

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

线程创建

三种创建方式:

  • Thread class(继承Thread类)
  • Runnable接口(实现Runnable接口)
  • Callable接口(实现Callable接口,了解即可)

继承Thread类

自定义线程类继承Thread类

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

创建线程对象,调用start()方法启动线程

java 复制代码
package ThreadStudy;
//继承Thread类
public class ThreadTest01 extends Thread{
    @Override
    public void run(){
        //run()方法线程体
        for(int i=0;i<10;i++){
            System.out.println("run"+i);
        }
    }

    public static void main(String[] args) {
        //main线程(主线程)
        //创建一个线程对象
        ThreadTest01 threadTest01 =new ThreadTest01();
        //调用start()方法开启线程
        threadTest01.start();

        for(int i=0;i<10;i++){
            System.out.println("主线程"+i);
        }
    }
}

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

案例:下载图片

java 复制代码
package ThreadStudy;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.spec.RSAOtherPrimeInfo;
//实现多线程同步下载图片
public class DownloadTest extends Thread{

    private String url;//图片地址
    private String name;//保存的文件名
    public DownloadTest(String url,String name){
        this.name=name;
        this.url=url;
    }
    @Override
    public void run(){
        webDownloader webDownloader=new webDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
    }

    public static void main(String[] args) {
        DownloadTest t1=new DownloadTest("https://img30.360buyimg.com/security/jfs/t1/106812/11/17157/47913/5e859436E81f1efd9/c6c4cf56305d75d2.jpg","1.jpg");
        DownloadTest t2=new DownloadTest("https://img30.360buyimg.com/security/jfs/t1/100763/10/17328/48613/5e85946eEd153e514/8ffec9a8e7832567.jpg","2.jpg");
        DownloadTest t3=new DownloadTest("https://img30.360buyimg.com/security/jfs/t1/98816/36/17526/48339/5e8594a8E4e154c27/42afb9370b45dab5.jpg","3jpg");

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

//下载器
class webDownloader{
    //下载方法
    public void downloader(String url,String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

可以看到下载不是按照顺序下载的

Runnable接口

定义MyRunnable类实现Rnnable接口

实现run()方法,编写线程执行体

创建线程对象,调用start()方法启动线程

java 复制代码
package ThreadStudy;

public class TestRunnable implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println("run"+i);
        }
    }

    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        TestRunnable testRunnable=new TestRunnable();

        //创建线程对象,通过线程对象开启线程,代理
        //相当于:
        //Thread thread=new thread(testRunnable);\
        //thread.start();
        new Thread(testRunnable).start();

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

使用Runnable接口实现下载图片:

只需要将继承Thread改为实现Runnable接口,再改一下start()部分即可

java 复制代码
package ThreadStudy;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.spec.RSAOtherPrimeInfo;
//实现多线程同步下载图片
public class DownloadTest implements Runnable{

    private String url;//图片地址
    private String name;//保存的文件名
    public DownloadTest(String url,String name){
        this.name=name;
        this.url=url;
    }
    @Override
    public void run(){
        webDownloader webDownloader=new webDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
    }

    public static void main(String[] args) {
        DownloadTest t1=new DownloadTest("https://img30.360buyimg.com/security/jfs/t1/106812/11/17157/47913/5e859436E81f1efd9/c6c4cf56305d75d2.jpg","1.jpg");
        DownloadTest t2=new DownloadTest("https://img30.360buyimg.com/security/jfs/t1/100763/10/17328/48613/5e85946eEd153e514/8ffec9a8e7832567.jpg","2.jpg");
        DownloadTest t3=new DownloadTest("https://img30.360buyimg.com/security/jfs/t1/98816/36/17526/48339/5e8594a8E4e154c27/42afb9370b45dab5.jpg","3jpg");

        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
    }
}

//下载器
class webDownloader{
    //下载方法
    public void downloader(String url,String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

小结:

继承Thread类

  • 子类继承Thread类,具备多线程能力
  • 启动线程:子类对象.start()
  • 不建议使用:避免OOP单继承局限性

实现Runnable接口

  • 实现接口Runnable具有多线程能力
  • 启动线程:传入目标对象+Thread(对象).start()
  • 推荐使用:避免单继承局限性,灵活,方便同一个对象被多个线程使用

多个线程同时操作同一个对象测试:

java 复制代码
package ThreadStudy;
//多个线程同时操作同一个对象
//模拟买票的例子
//发现问题:多个线程操作同一个资源时,线程不安全,数据紊乱
public class ThreadTest01 implements Runnable{

    private int ticketNums=10;

    @Override
    public void run(){
        while(true){
            if(ticketNums<1){
                break;
            }
            //模拟延时
            try{
                Thread.sleep(200);
            }catch(Exception e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
        }
    }

    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        ThreadTest01 ticket=new ThreadTest01();

        //创建线程对象,通过线程对象开启线程
        new Thread(ticket,"A").start();
        new Thread(ticket,"B").start();
        new Thread(ticket,"C").start();

    }
}

案例:龟兔赛跑

java 复制代码
package ThreadStudy;
//模拟龟兔赛跑
public class Race implements Runnable{
    private static String winner;
    @Override
    public void run(){
        for(int i=0;i<=100;i++){
            //模拟兔子休息
            if(Thread.currentThread().getName().equals("rabbit")&&i%10==0){
                try{
                    Thread.sleep(1);
                }catch(Exception 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;
        }{
            if(steps>=100){
                winner=Thread.currentThread().getName();
                System.out.println("winner is "+winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race=new Race();
        new Thread(race,"rabbit").start();
        new Thread(race,"turtle").start();
    }
}

实现callable接口(了解)

实现callable接口需要返回值类型

重写call方法,需要抛出异常

创建目标对象

创建执行服务:ExecutorService ser=Executors.newFixedThreadPool(1);

提交执行:Future result1=ser.submit(t1);

获取结果:boolean r1=result1.get()

关闭服务:ser.shutdownNow();

java 复制代码
package ThreadStudy;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.spec.RSAOtherPrimeInfo;
import java.util.concurrent.*;

//实现多线程同步下载图片
public class TestCallable implements Callable<Boolean> {

    private String url;//图片地址
    private String name;//保存的文件名
    public TestCallable(String url,String name){
        this.name=name;
        this.url=url;
    }
    @Override
    public Boolean call(){
        webDownloader webDownloader=new webDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1=new TestCallable("https://img30.360buyimg.com/security/jfs/t1/106812/11/17157/47913/5e859436E81f1efd9/c6c4cf56305d75d2.jpg","1.jpg");
        TestCallable t2=new TestCallable("https://img30.360buyimg.com/security/jfs/t1/100763/10/17328/48613/5e85946eEd153e514/8ffec9a8e7832567.jpg","2.jpg");
        TestCallable t3=new TestCallable("https://img30.360buyimg.com/security/jfs/t1/98816/36/17526/48339/5e8594a8E4e154c27/42afb9370b45dab5.jpg","3jpg");

        //创建执行服务
        ExecutorService ser= Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1=ser.submit(t1);
        Future<Boolean> r2=ser.submit(t2);
        Future<Boolean> r3=ser.submit(t3);
        //获取结果
        boolean rs1=r1.get();
        boolean rs2=r2.get();
        boolean rs3=r3.get();
        //关闭服务
        ser.shutdownNow();
    }
}

//下载器
class webDownloader{
    //下载方法
    public void downloader(String url,String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downloader方法出现问题");
        }
    }
}

静态代理

java 复制代码
package ThreadStudy;

//静态代理模式
public class StaticProxy {
    public static void main(String[] args) {
        WeddingCompany weddingcompany=new WeddingCompany(new You());
        weddingcompany.HappyMarry();
    }
}

interface Marry{
    void HappyMarry();
}

class You implements Marry{
    @Override
    public void HappyMarry(){
        System.out.println("happy");
    }
}

class WeddingCompany implements Marry{
    private Marry target;
    public WeddingCompany(Marry target){
        this.target=target;
    }
    @Override
    public void HappyMarry(){
        before();
        this.target.HappyMarry();
        after();
    }
    private void before(){
        System.out.println("结婚之前,布置现场");
    }
    private void after(){
        System.out.println("结婚之后,收尾款");
    }
}

静态代理总结:

真实对象和代理对象都要实现同一个目标,代理对象要代理真实角色

代理对象可以实现真实对象实现不了的功能,真实对象专注自己的功能

lamda表达式

为了避免匿名内部类定义过多,可以使代码看起来更简洁,其实质属于函数式编程。

**函数式接口定义:**只包含唯一一个抽象方法,那么它就是函数式接口。

java 复制代码
public interface Runnable{
    public abstract vvoid run();
}

对于函数式接口,可以通过lamda表达式创建该接口的对象。

格式:

java 复制代码
(params) -> expresssion[表达式]
    (params)->statement[语句]
    	(params)->{statements}
java 复制代码
package lamda;

/*
推导lamda表达式
*/
public class TestLamda {

    //2、静态内部类
    static class Like2 implements ILike{
        @Override
        public void lamda(){
            System.out.println("test lamda2");
        }
    }

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

        like =new Like2();
        like.lamda();

        //3、局部内部类
        class Like3 implements ILike{
            @Override
            public void lamda(){
                System.out.println("test lamda3");
            }
        }

        like=new Like3();
        like.lamda();

        //4、匿名内部类,没有类的名称,必须借助接口或父类
        like=new ILike(){
            @Override
            public void lamda(){
                System.out.println("test lamda4");
            }
        };
        like.lamda();

        //5、lamda简化
        like=()->{
            System.out.println("test lamda5");
        };
        like.lamda();

    }
}

//0、定义一个函数式接口
interface ILike{
    void lamda();
}

//1、实现类
class Like1 implements ILike{
    @Override
    public void lamda(){
        System.out.println("test lamda1");
    }
}

线程状态

线程方法

方法 说明
setPriority(int new Priority) 更改线程优先级
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
void join() 等待该线程终止
static void yield() 暂停当前正在执行的线程对象,并执行其他线程
void interrupt() 中断线程(尽量别用这个方式)
boolean isAlive() 测试线程是否处于活动状态
线程停止

尽量使线程自动停止(通过设置标记位)

线程休眠
线程礼让

线程强制执行

观测线程状态

线程优先级

相关推荐
c1assy7 分钟前
天机学堂3-ES+Caffeine
android·java·elasticsearch
xwz小王子9 分钟前
斯坦福大学李飞飞教授团队ARCap: 利用增强现实反馈收集高质量的人类示教以用于机器人学习
学习·机器人·ar
終不似少年遊*17 分钟前
通过一个算法的设计来了解栈的一些应用
java·前端·数据库
垂杨有暮鸦⊙_⊙19 分钟前
有限元分析学习——Anasys Workbanch第一阶段笔记(9)带孔矩形板与L型支架案例的对称平面处理方案
笔记·学习·有限元分析
军训猫猫头22 分钟前
43.Textbox的数据绑定 C#例子 WPF例子
java·c#·wpf
专注VB编程开发20年24 分钟前
c#有什么显示矢量图SVG的控件VB.NET-svg转透明PNG图像
开发语言·c#·.net·svg·矢量图
蹦蹦跳跳真可爱58939 分钟前
Python----Python基础(字符串,列表,元组,字典,集合的总结)
开发语言·python
灵性(๑>ڡ<)☆40 分钟前
Vue3学习-day4
前端·vue.js·学习
StudyWinter1 小时前
【OpenCV(C++)快速入门】--opencv学习
c++·学习·计算机视觉·openev