多线程学习笔记

本文详细介绍了Java多线程的原理、创建方式(继承Thread、实现Runnable和Callable)、线程状态、同步与协作,包括守护线程、死锁、线程池和锁的使用。通过实例演示了如何避免死锁,优化线程管理和资源竞争。
摘要由CSDN通过智能技术生成

本文内容为我学习【狂神说Java】多线程详解的笔记

多线程

普通方法与多线程的区别
在这里插入图片描述

Process和Thread

  • 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
  • 进程是执行程序的一次执行过程,是一个动态的概念。是系统资源分配的单位
  • 通常一个进程中可以包含多个线程,一个进程中至少有一个线程。线程是CPU调度和执行的单位(mian()方法就是主线程。gc线程)
    • ps:很多多线程是模拟出来的,真正的多线程是指由多个CPU,即多核

线程创建

在这里插入图片描述

方法一 继承Thread类
  1. 自定义线程类继承Thread类
  2. 重写run()方法,编写线程执行体
  3. 创建线程对象,调用**start()**方法启动线程
//创建线程方法一:继承Thread类,重写run()方法,调用start开启线程
public class TestThread01 extends Thread{
    @Override
    public void run() {
        //run方法线程体
        for(int i = 0; i < 20; i++){
            System.out.println("这是run方法第" +i+ "次");
        }
    }

    public static void main(String[] args) {

        TestThread01 testThread01 = new TestThread01();
        testThread01.start();

        //main线程
        for(int i = 0; i < 200; i++){//次数多一点,方便观察
            System.out.println("这是main方法第" +i+ "次");
        }
    }
}
方法二 实现Runnable(建议)
  1. 定义MyRunnable类实现Runnable接口(因为是接口,所以可以多继承;而且一个对象可以被多个线程使用(代理),这就是建议的原因)
  2. 实现run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
public class TestRunnable02 implements Runnable{

    @Override
    public void run() {
        //线程体
        for (int i = 0; i < 100; i++) {
            System.out.println("这里是run方法第" + i + "次");
        }
    }

    public static void main(String[] args) {

        //创建实现类对象
        TestRunnable02 testRunnable = new TestRunnable02();
        //创建代理类对象(new Thread(testRunnable).start();也可),     代理
        Thread thread = new Thread(testRunnable);
        thread.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("这里是main方法第" + i + "次");
        }
    }
}
方法三 实现Callable接口(了解)

在这里插入图片描述

import org.apache.commons.io.FileUtils;

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

public class TestCallable03 implements Callable<Boolean> {

    private String url;
    private String name;

    @Override
    public Boolean call() throws Exception {
        WebDownloader we1 = new WebDownloader();
        we1.downLoader(url,name);
        System.out.println("文件" + name + "下载成功");
        return true;
    }

    public TestCallable03(String url, String name){
        this.name = name;
        this.url = url;
    }

    public static void main(String[] args) {
        TestCallable03 t = new TestCallable03("https://mmbiz.qpic.cn/mmbiz_png/eicUicStt1WicPcGIB5a6u4TeHjZbP5fkib543QQSFa5ku4KD1O4cZ3A9vsvGgNeVcNCXf61oksZGgKbuGvhtHyxwQ/0?wx_fmt=png","callable.png");

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

       //提交执行
        Future<Boolean> result = ser.submit(t);
        //获取结果
        try {
            boolean newResult = result.get();//不知道为什么这句话会有异常,所以我用了try-catch,结果没问题
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //关闭服务
        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("downLoader出现异常");
        }
    }
}

静态代理

  • 真实对象和代理对象都要实现同一个接口
  • 代理对象要代理真实角色(所以要传参)

好处:
代理对象可以作真实对象做不了的事
真实对象专注做自己的事

public class StaticProxy04 {
    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("peach");
    }
}

//婚庆公司代理角色来帮助你
class WeddingCompany implements Marry{
    //代理对象
    private Marry target;

    public WeddingCompany(Marry target) {
        this.target = target;
    }

    @Override
    public void HappyMarry() {
        beforeWedding();
        this.target.HappyMarry();
        afterWedding();
    }

    private void beforeWedding(){
        System.out.println("结婚前");
    }

    private void afterWedding(){
        System.out.println("结婚后");
    }
}

Lambda表达式

  • 避免匿名内部类定义过多
  • 实质属于函数式编程的概念
    在这里插入图片描述
//推导Lambda表达式
public class TestLambda05 {

    //3.静态内部类
    static class Like2 implements ILike{
        @Override
        public void lambda() {
            System.out.println("lambda静态内部类");
        }
    }

    public static void main(String[] args) {

        //1、2
        ILike like = new Like();//父类的引用指向子类,由于子类重写父类的方法,所以执行子类的方法
        like.lambda();

        //3.
        like = new Like2();
        like.lambda();


        //4.局部内部类
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("lambda局部内部类");
            }
        }
        //4
        like = new Like3();
        like.lambda();


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

        //6.用lambda简化(删除5中new和lambda之间的单词,再加上->)
        like = ()->{System.out.println("lambda简化");};
        // ILike like2 = ()->{System.out.println("lambda");};//加上实例化对象一起
        like.lambda();

        //7.lambda还能简化
        /*ILove love = null;
        1.若方法只有一个参数,则可以再括号里直接写参数(不用写类型类型,参数类型由你调用该方法时用的参数类型确定)---love = (a)->{System.out.print("lambda");};
                         也可以不要括号love = a->{System.out.print("lambda");};
                有多个参数,则不能去掉括号,但是也可以去掉参数类型--love = (a,b)->{System.out.print("lambda");};
        2.若方法体只有一句话,则可以去掉花括号和一个分号---love = a->System.out.print("lambda");

         */



    }
}

//1.先定义一个函数式接口(只有一个方法)
interface ILike{
    void lambda();
}

//2.实现类
class Like implements ILike{
    @Override
    public void lambda() {
        System.out.println("lambda");
    }
}

线程状态

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

线程停止
  • 不推荐使用JDK提供的stop()\destroy()方法(已废弃)
  • 推荐线程自己停止下来
  • 建议使用一个标志位进行终止变量
public class TestStop06 implements Runnable{

    //设置标志位
    private Boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println("This is run Thread " + (i++));
        }
    }

    //设置一个公开的方法停止线程(转换标志位)
    public void stop(){
        this.flag = false;
    }

    public static void main(String[] args) {

        TestStop06 testStop = new TestStop06();
        new Thread(testStop).start();

        for (int i = 0; i < 100; i++) {
            System.out.println("This is main Thread" + i);//???为什么main方法里如果没有内容循环输出,namrun方法了也不会输出内容
            if (i==99){
                //调用stop方法停止线程
                testStop.stop();
                System.out.println("线程停止");
            }
        }
    }
}
线程休眠(Thread.sleep())

在这里插入图片描述

  • 注意sleep存在异常,所以要自己捕获
//模拟倒计时
public class TestSleep2 {

    //写成静态方法直接调用
   public static void tenSecondsDown() throws InterruptedException {
       int time = 10;

       while(true){
           Thread.sleep(1000);
           System.out.println(time--);
           if (time == 0){
               break;
           }
       }
    }

    public static void main(String[] args) {

        try {
            tenSecondsDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
线程礼让(Thread.yield())

在这里插入图片描述

//礼让不一定成功,要看CPU调度
public class TestYield {
    public static void main(String[] args) {

        MyYield myYield = new MyYield();

        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}


class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println("MyYield-->" +Thread.currentThread().getName()+ "开始执行");
        Thread.yield(); //礼让
        System.out.println("MyYield-->" +Thread.currentThread().getName()+"停止执行");
    }
}
线程强制执行(thread.join())
  • Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞(想像成插队)
//注意不要使用我注释了的匿名初始化,这样会使join失败
//结果:先执行main方法至i=200,然后执行 完 所有的run方法,之后再执行剩余的main方法
//所有这样理解join:join语句在那个方法里,达到一定条件后,该方法就要让别的方法先走
public class TestJoin implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("run-->" + i);
        }
    }


    public static void main(String[] args) {
        TestJoin testJoin = new TestJoin();
//        new Thread(testJoin).start();

        Thread thread = new Thread(testJoin);
        thread.start();

        for (int i = 0; i < 500; i++) {
            if(i == 200){
                try {
//                    new Thread().join(); //注意这里要初始化
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("main-->" + i);
        }
    }
}
线程状态的观测
//观察测试线程的状态
public class TestViewState {
    public static void main(String[] args) {

        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("/");
            }
        });

        //观察状态
        Thread.State  state = thread.getState();
        System.out.println(state);//结果:NEW  (线程处于创建状态)

        //启动线程
        thread.start();

        //更新线程状态
        state = thread.getState();
        System.out.println(state);//RUNNABLE  (线程处于运行状态)

        while(state != Thread.State.TERMINATED){//只要线程不终止,就一直输出
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //更新
            state = thread.getState();//前面有sleep,所以此时为TIMED_WAITING
            System.out.println(state);
        }

    }
}

线程的优先级

在这里插入图片描述

//测试线程的优先级-->一般优先级高(数字大)的会先走(但这是理想情况,现实下不一定:优先级高-->被CPU调用的 概率 高)
public class TestPriority {

    public static void main(String[] args) {
        //主线程默认优先级
        System.out.println(Thread.currentThread().getName()+ "-->" +Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();

        Thread th1 = new Thread(myPriority,"th1");
        Thread th2 = new Thread(myPriority,"th2");
        Thread th3 = new Thread(myPriority,"th3");
        Thread th4 = new Thread(myPriority,"th4");


        //先设置优先级,再启动
        th1.start();

        th2.setPriority(1);
        th2.start();

        th3.setPriority(4);
        th3.start();

        th4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY = 10
        th4.start();

    }
}

class MyPriority implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+ "-->" +Thread.currentThread().getPriority());
    }
}

守护线程(thread.setDaemon(true))

在这里插入图片描述

//测试守护线程-->结果,用户线程结束后,守护线程跑了议会也停止了(JVM停止需要时间)
//上帝守护你
public class TestDaemon {
    public static void main(String[] args) {

        God god = new God();
        YouGuy youGuy = new YouGuy();

        Thread thread = new Thread(god);
        thread.setDaemon(true); //默认是false, 表示是用户线程.正常的线程都是用户线程

        thread.start();//上帝守护线程启动

        new Thread(youGuy).start();//你 用户线程 启动
    }
}

//上帝
class God implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("God is here now and then.");
        }
    }
}

//你
class YouGuy implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("你活了 " +i+ " 天");
        }

        System.out.println("-------you died-----");
    }
}

线程同步

  • 多个线程操作同一资源

并发: 同一个对象多个线程 同时操作(访问)
在这里插入图片描述

线程同步形成条件: 队列+锁
在这里插入图片描述

同步方法

在这里插入图片描述

同步块

在这里插入图片描述

  • Obj是有增删改的对象

死锁

在这里插入图片描述

//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
    public static void main(String[] args) {

        Makeup girl1 = new Makeup(0,"girl1");
        Makeup girl2 = new Makeup(1,"girl2");

        girl1.start();
        girl2.start();
    }
}

//口红
class Lipstick{

}

//镜子
class Mirror{

}

//化妆
class Makeup extends Thread{
    //需要的资源只有一份,用static保证
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;
    String name;//化妆的人

    public Makeup(int choice, String name){
        this.choice = choice;
        this.name = name;
    }

    @Override
    public void run() {
        //化妆
        makeup();
    }
    private void makeup(){
        if(choice==0){
            synchronized (lipstick){//获得口红的锁
                System.out.println(this.name+ "获得口红的锁");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            synchronized (mirror){//1秒后想获得镜子的锁
                System.out.println(this.name+ "获得镜子的锁");
            }
        }else{
            synchronized (mirror){//获得镜子的锁
                System.out.println(this.name+ "获得镜子的锁");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            synchronized (lipstick){//1秒后想获得口红的锁
                System.out.println(this.name+ "获得口红的锁");
            }
}
     }
}
  • 上面的代码中,53行synchronized整个语句如果放在44行synchronized语句体里面, 66行synchronized整个语句如果放在57行synchronized语句体里面, 这样就会造成死锁

lock锁

在这里插入图片描述

import java.util.concurrent.locks.ReentrantLock;

//测试lock锁
public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();

        new Thread(testLock2,"Lock2 一").start();
        new Thread(testLock2,"Lock2 二").start();
        new Thread(testLock2,"Lock2 三").start();
    }
}

class TestLock2 implements Runnable{
    private int ticketNums = 10;

    //定义lock锁
    private final ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public  void run() {
        while(true){

            try {//最外围加try-finally是为了给每个进入循环的线程加\解锁
                reentrantLock.lock();//加锁

                if (ticketNums>0){
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+ "拿到了第" +(ticketNums--)+ "张票");

                }else{
                    System.out.println("票售尽");
                    break;
                }

            } finally {
                reentrantLock.unlock();//解锁
            }
        }
    }
}
  • 由学过的同步方法知道,上面的代码中,也可以不加lock锁,直接在重写方法中加一个synchronized也能解决并发延时问题

在这里插入图片描述

在这里插入图片描述

线程协作

线程通信

在这里插入图片描述

在这里插入图片描述

解决方式一: 管程法

// 管程法 解决生产者消费者模型(利用缓存区)
//生产者\消费者\产品\缓冲区
public class SolutionPC01 {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Producer(container).start();
        new Consumer(container).start();

    }
}

//生产者
class Producer extends Thread{
    SynContainer container;

    public Producer(SynContainer container){
        this.container = container;
    }

    //生产

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            container.push(new Production(i));
            System.out.println("生产了"+i+"只鸡");
        }
    }
}

//消费者
class Consumer extends Thread{
    SynContainer container;

    public Consumer(SynContainer container){
        this.container = container;
    }

    //消费

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println("消费了-->"+container.pop().id+"只鸡");
        }
    }
}

//产品
class Production{
    int id; //产品编号

    public Production(int id) {
        this.id = id;
    }
}

//缓冲区
class SynContainer{

    //容器大小
    Production[] pros = new Production[10];
    //容器计数器
    int count = 0;

    //生产者放入产品
    public synchronized void push(Production pro){
        //容器满了
        if (count == pros.length) {
            System.out.println("the synContainer is full");
            try {
                this.wait();//生产者等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //容器未满-->丢入production
        pros[count] = pro;
        count++;

        //通知消费者消费
        this.notifyAll();
    }

    //消费者消费production
    public synchronized Production pop(){
        //判断能否消费
        if (count==0){
            try {
                this.wait();//消费者等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        count--;
        Production pro = pros[count];
        //通知生产者生产
        this.notifyAll();
        return pro;

    }
}
解决方式二:

在这里插入图片描述

//信号灯法-->设置标志位解决
public class SolutionPC02 {
    public static void main(String[] args) {
        Show show = new Show();
        new Actor(show).start();
        new Audience(show).start();
    }
}


//演员
class Actor extends Thread{

    Show show;

    public Actor(Show show){
        this.show = show;
    }

    @Override
    public void run() {
        for (int i = 0; i <20 ; i++){
            if(i%2==0) {
                this.show.play("佚名");
            }else{
                this.show.play("广告");
            }
        }
    }
}

//观众
class Audience extends Thread{
    Show show;

    public Audience(Show show){
        this.show = show;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            show.watch(i);
        }
    }
}

//节目
class Show{
    //演员表演,观众等待-->true
    //观众观看,演员等待-->false
    String program; //表演的节目
    boolean flag = true;


    //演员表演
    public synchronized void play(String program){
        if(!flag){
            try { //观众观看,演员等待-->false
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("演员表演了节目:" + program);

        //通知观众观看
        this.notifyAll();
        this.program = program;
        this.flag = !this.flag;
    }

    //观众看
    public synchronized void watch(int i){
        if(flag){//演员表演,观众等待-->true
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了节目:" + program);
        System.out.println("------" + (i + 1) + "-----");

        //通知演员表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}
线程池

在这里插入图片描述

在这里插入图片描述

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

//测试线程池
public class threadPool {
    public static void main(String[] args) {
        //1.创建服务,创建线程池(newFixedThreadPool()中参数为池子大小)
        ExecutorService service = Executors.newFixedThreadPool(10);

        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //2.关闭连接
        service.shutdown();
    }
}

class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值