java多线程

一、线程、进程

见操作系统

任务:进程在进入内存之前叫做任务

进程:用户视角进程即程序的一次执行过程,操作系统视角是操作系统分配资源的最小单位,进程包括代码段、数据段和进程控制块PCB,进程有动态性、并发性、独立性、异步性的特征,进程的状态包括就绪态、运行态、阻塞态、就绪挂起态、就绪阻塞态,其中就绪态是已经准备完毕等待cpu代用,阻塞态是等待某些I/O操作,挂起态是把进程从内存拿到磁盘中的状态

线程:一个进程可以分为多个线程,共享进程内的数据,是cpu最小调度单位

image-20220120103110707

image-20220120103446592

二、线程创建

image-20220120103701439

1、Thread类

image-20220120112250742

package Thread;
//继承Thread类,重写run()方法
public class a extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("多线程--"+i);
        }
    }
    public static void main(String[] args) {
        a a = new a();
        //start()方法开启多线程,会让run()方法和main()方法调度并发执行
        //线程开启不一定立即执行,看cpu调度,不确定性
        a.start();
        //run()方法相当于普通调用函数,按照代码结构顺序执行
//        a.run();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程--"+i);
        }
    }
}
concle
    输出不定,线程调度执行

image-20220120113655211

2、Runnable接口

image-20220120115400678

package Thread;
//类似Thread子类继承方法,调用Runnable接口,重写run()方法,作为多线程的内容
public class b implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("多线程--"+i);
        }
    }

    public static void main(String[] args) {
        b b = new b();
        //不同于Thread方法,打开线程,创建Thread的对象,使用Thread的start()实现打开线程
        //创建Thread的对象时,把线程类的id现在参数中
//        Thread thread = new Thread(b);
        //使用thread.start()打开线程
//        thread.start();
        //上面两句代码可以整合为下面一句代码
        (new Thread(b)).start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程--"+i);
        }
    }
}

Thread继承子类的方法,一个对象就是一个线程

Runnable接口方法,一个对象可以调整对象参数设置为多个线程

image-20220120122440784

3、并发问题

并发问题就是指多个线程并发执行,可能导致的数据访问问题和代码执行顺序问题,cpu一个调度周期内不能完成线程的执行,线程就要等待再次cpu调度执行

package Thread;

public class c implements Runnable {
    private int ticket = 10;
    @Override
    public void run() {

        while(true) {
            if (ticket < 1) {
                break;
            }
//这里设计一个延时,减慢程序运行的速度,本质让线程在一个cpu调度周期不能完成,显示并发问题
            //就是数据访问问题和执行顺序不确定问题
            try {
                Thread.sleep(200);
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+" 拿到了第 "+ticket--+"张票!");
        }
    }

    public static void main(String[] args) {
        c c = new c();
        new Thread(c,"小明").start();
        new Thread(c,"老师").start();
        new Thread(c,"学生").start();
    }
}
concle
    结果不确定,就连第i个数据的i都可能不是递减顺序,因为cpu调度时间问题,还可能出现-1的情况
4、Callable类

image-20220120165245293

package Thread;

import java.util.concurrent.*;

public class e implements Callable {
    String a;
    public e(String a) {
        this.a = a;
    }
    @Override
    public Boolean call() throws Exception {
        System.out.println(a);
        return true;
    }

    public static void main(String[] args) throws ExecutionException,InterruptedException{
        //类似Thread类继承
        e e1 = new e("多线程");
        e e2 = new e("主线程");
        //创建执行服务,并指出进程个数Executors.newFixedThreadPool(进程个数)
        ExecutorService ser = Executors.newFixedThreadPool(2);
        //提交执行任务
        Future<Boolean> result1 = ser.submit(e1);
        Future<Boolean> result2 = ser.submit(e2);
        //获取结果,根据设计的return值
        boolean r1 = result1.get();
        boolean r2 = result2.get();
        //关闭任务
        ser.shutdownNow();

    }
}
三、线程管理
1、静态代理

1、静态代理分为真实类和代理类,要指向同一个接口

2、静态代理的意思就是设计一个真实类和代理类,代理类执行主类函数,进而代理对象执行

package Thread;

public class a1 {
    public static void main(String[] args) {
        b2 b2 = new b2(new b1());
        b2.marry();
    }
}
interface marry {
    void marry();
}
//创建真实角色
class b1 implements marry {
    @Override
    public void marry() {
        System.out.println("婚礼");
    }
}
//创建代理角色
class b2 implements marry {
    //创建一个真实类的对象,方便函数的调用
    b1 target;
    public b2(b1 target) {
        this.target = target;
    }

    @Override
    public void marry() {
        before();
        target.marry();
        after();
    }

    void before() {
        System.out.println("准备");
    }

    void after() {
        System.out.println("数钱");
    }
}
concle
    准备
    婚礼
    数钱
//使用Runnable接口时,Thread就是一个代理,Thread源码本身就指向Runnable接口
2、Lamda表达式

image-20220120203344562

Lamda表达式本质是为了简化代码

1、Lamda表达式只能用于接口定义一个函数的函数式接口

2、Lamda表达式当有一个参数时,可以去掉(),但是当多个参数时,不能去掉()

3、参数前面的类型定义可以去掉,当多个参数时,要么都去掉,要么都不去掉

4、函数部分{}内的代码当只有一行时可以去掉{},多行时不能去掉{}

5、Lamda表达式在定义时,可以使用 接口id 对象id = Lamda表达式

或者 接口id 对象id = null 接着定义Lamda表达式

package Thread;

public class Lamada1 {
    public static void main(String[] args) {
        //这是正常使用指向接口的类执行类中函数的方式
//        Ilove love = new love();
//        love.love(2);
        //1、Lamda表达式定义时,可以设置为空,接着设置函数
//        Ilove love = null;
        //2、Lamda表达式定义时,可以直接后面接函数设置
        Ilove love = (int a)->{
            System.out.println("i love you->"+a);
        };
        love.love(3);
//1、省去类型定义
        Ilove love1 = (a)->{
            System.out.println("i love you->"+a);
        };
        love1.love(521);
//2、省去()
//        Ilove love2 = null;
        Ilove love2 = a->{
            System.out.println("i love you->"+a);
        };
        love2.love(12);
//省去{},当函数内代码一行
        Ilove love3 = a-> System.out.println("i love you->"+a);
        love3.love(5);
    }
}
//使用Lamda表达式时,只用设置接口,不用定义接口的类,但是接口只能有一个函数
interface Ilove {
    void love(int a);
}

//class love implements Ilove {
//    @Override
//    public void love(int a) {
//        System.out.println("i love you->"+a);
//    }
//}
concle
    i love you->3
    i love you->521
    i love you->12
    i love you->5
3、线程状态

线程状态

image-20220120222740301

image-20220120222832318

线程方法

image-20220120222934624

4、线程停止stop

image-20220120223226792

package Thread_1;

public class a implements Runnable{
    //设置一个标志位,当线程满足默写情况时,修改标志为来终止线程
    private Boolean flag = true;
    @Override
    public void run() {
        int i =0;
        while(flag) {
            System.out.println("run  Thread->"+i++);
        }
    }
//自定义的终止函数,修改标志位
    public void stop() {
        this.flag = false;
    }

    public static void main(String[] args) {
        a ab = new a();
        new Thread(ab).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("mmain Thread->"+i);
            //当线程满足一定情况时,调用终止函数停止线程
            if (i==90) {
                ab.stop();
                System.out.println("线程停止");
            }
        }
    }
}
5、线程休眠sleep

package Thread_1;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.SimpleFormatter;

public class b {
    public static void main(String[] args) {
        //1、这是根据创建的类倒计时的代码
//        try {
//            tenDown();
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        
        //2、每隔一秒获取当前时间的代码
        //获取当前时间
        Date datetime= new Date(System.currentTimeMillis());
        while(true) {
            try{
                //使用sleep每隔一秒输出一次当前时间
                Thread.sleep(1000);
                //输出当前时间,时间格式
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(datetime));
                //刷新当前时间
                datetime = new Date(System.currentTimeMillis());
            }catch(InterruptedException e) {
            e.printStackTrace();
            }
        }
    }
    public static void tenDown() throws InterruptedException {
        int time = 10;
        while(true) {
            Thread.sleep(1000);
            System.out.println(time--);
            if (time<=0) {
                break;
            }
        }
    }
}
6、礼让线程yield

image-20220121103645702

7、线程强制执行join

image-20220121103731516

jion会让调用jion的多线程获得cpu,先执行

package Thread_1;
//jion调用之前,主线程和多线程的运行随机,不确定性,调用join,调用的多线程运行
public class c extends Thread{
    //多线程主体
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("多线程->"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        c c = new c();
//        Thread thread = new Thread(c);
//        thread.start();
        c.start();
        for (int i = 0; i < 20; i++) {
            //调用jion,停止主线程运行,让调用jion的多线程运行到
            if (i==10) {
                //jion使用要抛出异常
                c.join();
            }
            System.out.println("main线程->"+i);
        }
    }
}
//class d extends Thread {
//    @Override
//    public void run() {
//        super.run();
//    }
//}
8、检测线程状态state

检测线程当前处于什么状态

image-20220121111010973

9、线程的优先级

image-20220121141539252

package Thread_1;

public class e implements Runnable {
    @Override
    public void run() {
        //多线程的id从   Thread-0  计算
        System.out.println("多线程 "+Thread.currentThread().getName()+" 的优先级->"+Thread.currentThread().getPriority());
    }

    public static void main(String[] args) {
        //输出主线程的默认优先级
        //线程的优先级默认是5
        System.out.println("主线程的优先级-> "+Thread.currentThread().getPriority());
        e e = new e();
        Thread e1 = new Thread(e);
        Thread e2 = new Thread(e);
        Thread e3 = new Thread(e);
        Thread e4 = new Thread(e);
        Thread e5 = new Thread(e);
        Thread e6 = new Thread(e);
        //输出多线程的默认优先级
        e1.start();
        //将多线程的默认优先级设置为最大优先级,10
        e2.setPriority(Thread.MAX_PRIORITY);
        e2.start();
        //先设计优先级再启动
        e3.setPriority(1);
        e3.start();

        e4.setPriority(2);
        e4.start();

        e5.setPriority(4);
        e5.start();

        e6.setPriority(6);
        e6.start();
    }
}
concle
    即便线程优先级设计很高,根据操作系统调度原理,不一定按照优先级执行
10、守护线程

image-20220121152153637

public class a {
    b b = new b();
    Thread thread = new Thread(b);
    //默认为false,即用户线程,true变为守护线程
    thread.Daemon(true);
    thread.start();
}
class b implements Runnable {
    
}//守护线程在其他线程结束就会停止
四、线程同步

多个线程同时访问同一个资源,需要设计访问顺序和访问条件,防止并发访问导致数据错误

1、线程同步机制
2、线程同步方法

image-20220121162012807

image-20220121162250932

1、synchronized方法就是在方法的定义上添加synchronized修饰符,让整个类的对象变成临界区,见操作系统

2、synchronized块是指将一个对象变成临界区,可以将变化的数据单独设为一个类,在方法调用时,直接在要使用变化数据的代码外面加入一个synchronized的块,就可以单独设置这个块内代码为临界区

image-20220121163331085

image-20220121163500318

package lock;

public class a {
    public static void main(String[] args) throws InterruptedException {
        int a = 100;
        int b = 0;
        a2 a2 = new a2(a,b);
        a1 a1 = new a1(a2);
        Thread thread1 = new Thread(a1,"thread1");
        Thread thread2 = new Thread(a1,"thread2");
        Thread thread3 = new Thread(a1,"thread3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}
//将变化的数据设置为单独的一个快,在synchronized块方法中,使用
class a2 {
    int a;
    int b;
    public a2(int a,int b) {
        this.a = a;
        this.b = b;
    }
}
//定义多线程的类
class a1 implements Runnable {
//    int a;
//    int b;
//    a1 c = new a1(a,b);
//    public a1(int a,int b) {
//        this.a = a;
//        this.b = b;
//    }
    a2 a2;
    public a1(a2 a2) {
        this.a2 =a2;
    }
    @Override
    public void run() {

        try {
            get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


    public void get() throws InterruptedException {
        //这是synchronized块的使用方法,操作对象是一个对象,就是变化数据的类的对象
        //在函数中找到使用变化数据的代码段,外面加入synchronized块,就能让这个块变成临界区
        synchronized (a2) {
            if (a2.a<=0){
                System.out.println(Thread.currentThread().getName()+" 没钱了");
                return;
            }

            Thread.sleep(1000);

            a2.a-=50;
            a2.b+=50;
            System.out.println(Thread.currentThread().getName()+" "+a2.a);
            System.out.println("手里有"+a2.b+"元钱");
        }

    }
//这是synchronized方法的使用,在方法的定义中加入synchronized的声明,让多线程整个变成临界区
//    public synchronized void get() throws InterruptedException {
//        if (a<=0){
//            System.out.println(Thread.currentThread().getName()+" 没钱了");
//            return;
//        }
//
//        Thread.sleep(1000);
//
//        a-=50;
//        b+=50;
//        System.out.println(Thread.currentThread().getName()+" "+a);
//        System.out.println("手里有"+b+"元钱");
//    }

}
3、死锁

两种进程因为抢夺同一种资源而相互等待都不执行的情况

image-20220123101026074

注:类中的数据前面加上static之后,当创建两个这个类的对象时,数据可能被覆盖

数据覆盖

package lock;

public class c {
    public static void main(String[] args) {
        c1 c1 = new c1(1);
        c1 c2 = new c1(2);
        Thread thread1 = new Thread(c1);
        Thread thread2 = new Thread(c2);
        thread1.start();
        thread2.start();
    }
}

class c1 implements Runnable {
    //加上static,可能出现数据覆盖
    static int a;
    c1(int a) {
        this.a = a;
    }
    @Override
    public void run() {
        System.out.println(a);
    }
}
concle
    2
    2

死锁

package lock;

public class b {
    public static void main(String[] args) {
        bb b1 = new bb(0,"girl1");
        bb b2 = new bb(1,"girl2");
        Thread thread1 = new Thread(b1);
        Thread thread2 = new Thread(b2);
        //模拟死锁
        thread1.start();
        thread2.start();
    }

}
//口红
class b1 {

}
//镜子
class b2 {

}
class bb implements Runnable {
    static b1 b1 = new b1();
    static b2 b2 = new b2();

    int choice;
    String girlName;

    public bb(int choice,String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        makeup();
    }
//synchronized里面嵌套synchronized来构造死锁
//修改死锁的方法:把synchronized的嵌套变成非嵌套
    private void makeup() {
        if (this.choice == 0) {
            synchronized (b1) {
                System.out.println(this.girlName+"获得口红");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b2) {
                    System.out.println(this.girlName+"获得镜子");
                }
            }
        }
        else {
            synchronized (b2) {
                System.out.println(this.girlName+"获得镜子");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b1) {
                    System.out.println(this.girlName+"获得口红");
                }
            }
        }
    }
}
concle
    girl1获得口红
    girl2获得镜子

image-20220123115302587

4、Lock锁

image-20220123115410893

image-20220123155223975

image-20220123155245734

更像PV操作

package lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class d {
    public static void main(String[] args) {
        d1 d1 = new d1();
        new Thread(d1).start();
        new Thread(d1).start();
        new Thread(d1).start();
    }
}
class d1 implements Runnable {
    private static int a = 10;
    //创建一个lock锁类的对象
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        try {
            get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    void get() throws InterruptedException {
        while(true) {
            //lock锁一般放在try finally中使用
            //try中上锁以及要锁的代码
            try{
                lock.lock();
                if (a>=0) {
                    try{
                        Thread.sleep(1000);
                        System.out.println(a--);
                    }catch(InternalError e){
                        e.printStackTrace();
                    }
                }
                else
                {
                    break;
                }
                //finally开锁
            }finally {
                lock.unlock();
            }
        }

    }
}

image-20220123161009940

五、生产者消费者
package lock;

import java.util.concurrent.locks.ReentrantLock;

public class e {
    public static void main(String[] args) {
        food food = new food();
        food.food = 2;
        produce produce = new produce();
        customer customer = new customer();
        //一个生产者
        new Thread(produce,"produce1").start();
        //两个消费者
        new Thread(customer,"customer1").start();
        new Thread(customer,"customer2").start();
//        System.out.println(food.food);
    }
}

class food {
    static int food = 2;
    static ReentrantLock lock = new ReentrantLock();
    public food() {
        this.food = food;
    }
}
class produce implements Runnable {
    food food = new food();
//    int a = food.food;
    @Override
    public void run() {
        while(true) {
//            System.out.println(food.food);
            try{
                food.lock.lock();
                if (food.food == 0) {
                    System.out.println("当前food = "+food.food+++"生产者生产food");
                    System.out.println("当前food = "+food.food+++"生产者生产food");

                }
            }
            finally {
                food.lock.unlock();
            }
//            synchronized (food) {
//
//            }
        }


    }
}

class customer implements Runnable {
    food food = new food();
//    static ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true) {

                try{
                    food.lock.lock();
                    if (food.food >= 1) {
                        try {
                            Thread.sleep(1000);
                            System.out.println("当前food = "+food.food+",消费者" + Thread.currentThread().getName() + "吃food,当前food = " + --food.food);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }finally{
                    food.lock.unlock();
                }

        }
    }
}

同时可以使用线程通信的方法

1、线程通信的方法

2、信号灯法

设置一个标志位,操作系统进程同步的value

3、线程池

image-20220123195457502

image-20220123202808598

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值