多线程详解

一、线程简介

比如在看一个视频的时候,声音、字幕、图像都是不同的线程,共同构成一个进程(视频)

真正的多线程是指有多个cpu,即多核,如服务器
而模拟出来的多线程,是一个cpu(调度器)的情况下,来回切换,因为切换的很快所以看不出来,但在同一个时间点,cpu只能执行一行代码

注意的点:
(1)对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
(2)线程会带来额外的开销,如cpu调度时间,并发控制开销

二、线程的创建

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

1、继承Thread类
步骤:
自定义线程类继承Thread类
重写run()方法
创建线程对象,调用start()方法启动线程
在这里插入图片描述
run()方法是先执行主方法,再执行后面的run()方法

start()方法是多条执行路径交替执行的

public class StartThread1 extends Thread{
    public void run(){
        for(int i = 0;i < 10;i++){
            System.out.println(i + "我在学习多线程");
        }
    }

    public static void main(String[] args) {
        StartThread1 uu = new StartThread1();
        uu.start();//主方法和run方法的内容就会交替执行
        uu.run();//先执行主方法,再执行run方法

        for(int i = 0;i < 2000;i++){
            System.out.println(i + "希望之后能学懂");
        }

    }
}

2、实现Runnable接口

定义MyRunnable类实现Runnable接口
重写run()方法
创建线程对象,调用start()方法启动线程

public class StartThread2 implements Runnable{
    public void run(){
        for(int i = 0;i < 10;i++){
            System.out.println(i + "我在学习多线程");
        }
    }

    public static void main(String[] args) {
        StartThread2 yu = new StartThread2();
        new Thread(yu).start();//执行线程需要丢入Runnable

        for(int i = 0;i < 2000;i++){
            System.out.println(i + "希望之后能学懂");
        }
    }

}

多个线程同时操作同一个对象

//多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class StartThread3 implements Runnable{
    private int piaoshu = 10;

    public void run(){
        while (true){
            if(piaoshu <= 0){
                break;
            }
            try {
                Thread.sleep(200);//延时,之所以延时就是因为太快了,分辨不出来了
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "拿到了第" + piaoshu +"票");
        }
    }

    public static void main(String[] args) {
        StartThread3 ii = new StartThread3();

        new Thread(ii,"小明").start();
        new Thread(ii,"老师").start();
        new Thread(ii,"程序员").start();
    }
}

3、实现Callable接口

静态代理模式:
真实对象和代理对象都要实现同一个接口
代理对象要代理真实对象

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

Lamda表达式

public class TestLamda1 {

    //3、静态内部类
    static class Like2 implements ILike{
        public void lamda(){
            System.out.println("i love lamda3");
        }
    }


    public static void main(String[] args) {
        ILike uu = new Like();//外部实现类
        uu.lamda();

        ILike ii = new Like2();//静态内部类的实现方法
        ii.lamda();

        //4、局部内部类
        class Like3 implements ILike{
            public void lamda(){
                System.out.println("i love lamda4");
            }
        }
        ILike gg = new Like3();
        gg.lamda();


        //5、匿名内部类
        ILike ww = new Like() {
            public void lamda(){
                System.out.println("i like lamda5");
            }
        };
        ww.lamda();

        //6、用lamda简化
        ILike qq = () ->{
            System.out.println("i like lamda6");
        };
        qq.lamda();
    }
}

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

//2、实现类
class Like implements ILike{
    public void lamda(){
        System.out.println("i love lamda2");
    }
}
//lamda表达式
// 前提是函数式接口
public class TestLamda2 {
    public static void main(String[] args) {

        ILove rr = (a,b,c) -> {//重写对象的实现方法去执行
            System.out.println("i love you -->" + a);
            System.out.println("too");
        };

        rr.love(520,502,250);
    }
}


//定义一个函数式接口(只有唯一一个抽象方法的接口是函数式接口)
interface ILove{
    void love(int a,int b,int c);
}

三、线程的状态

在这里插入图片描述
线程停止
线程休眠 sleep
线程礼让 yield

1000.for(快捷键)

线程同步:多个线程操作同一个资源的时候用这项技术
并发:同一个对象被多个线程同时操作

这个时候我们就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入对象的等待池形成队列,等前面的执行完,下一个再使用

要保证线程同步的安全性,就需要线程+锁
在这里插入图片描述
相当于1000个线程过来,只有一个线程能进入操作,那为了保证它的安全,那就给它加锁,关上门它做完自己的事了,那就轮到其他的线程来了,同样的加锁

在这里插入图片描述
同步方法和同步块都能解决不安全的问题

同步方法和同步代码块的区别

区别:
同步方法默认用this或者当前类class对象作为锁;
同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法;
同步方法使用关键字 synchronized修饰方法,而同步代码块主要是修饰需要进行同步的代码,用 synchronized(object){代码内容}进行修饰;

为何使用同步?
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(增删改查),将会导致数据的不准确,相互之间产生冲突。类似于在atm取钱,银行数据确没有变,这是不行的,要存在于一个事务中。因此加入了同步锁,以避免在该线程没有结束前,调用其他线程。从而保证了变量的唯一性,准确性。
1.同步方法:
即有synchronized修饰符修饰的方法。
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用给方法前,要获取内置锁,否则处于阻塞状态。
例:public synchronized getMoney(){}
注:synchronized修饰静态方法,如果调用该静态方法,将锁住整个类。

2.同步代码块
即有synchronized修饰符修饰的语句块,被该关键词修饰的语句块,将加上内置锁。实现同步。
例:synchronized(Object o){}

同步是高开销的操作,因此尽量减少同步的内容。通常没有必要同步整个方法,同步部分代码块即可。
同步方法默认用this或者当前类class对象作为锁。
同步代码块可以选择以什么来加锁,比同步方法要更颗粒化,我们可以选择只同步会发生问题的部分代码而不是整个方法。

什么是JUC:

java.util.concurrent包名的简写,是关于并发编程的API
与JUC相关的有三个包:java.util.concurrent、java.util.concurrent.atomic、java.util.concurrent.locks。
在这里插入图片描述
volatile、ReentrantLock

四、死锁

两个或者多个线程各自占有一些共享资源,都在等待对方释放资源;某一个同步块同时拥有两个以上对象的锁时,会发生死锁

在这里插入图片描述
拿到镜子的人要去拿口红,拿到口红的人要去拿镜子

在这里插入图片描述

在这里插入图片描述

五、生产者和消费者模式(线程协作)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解决线程通信的两种方式:管程法和信号灯法

1、管程法
用notify()互相通知
在这里插入图片描述

2、信号灯法
标志位来解决,true等待,false通知
在这里插入图片描述

这两种方法主要要解决什么时候通知,什么时候等待的问题

六、线程池

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值