JAVA多线程

一,进程和线程

相关概念

 - 进程

1.进程指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间。
2,进程是程序的一次执行过程,或是正在运行的一个程序,是动态过程:有它自身的产生,存在和消亡的过程

 - 线程

1.线程由进程创建的,是进程的一个实体
2.一个进程可以拥有多个线程

 - 其他相关概念

*单线程*:同一个时刻,只允许执行一个线程
*多线程*:同一个时刻,可以执行多个线程

*并发*:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核CPU实现的多任务就是并发
*并行*:同一个时刻,多个任务同时执行,多核cpu可以实现并行
存在并发和并行同时存在

二,线程基本使用

*创建线程的两种方式*Java中线程的使用有两种方法
1.继承Thread类,重写run方法
2.实现Runnable接口,重写run方法
public class Thread01 {
    public static void main(String[] args) {
        // 创建cat对象,当作线程使用
        Cat cat = new Cat();
        cat.start();
        // cat.run()就是一个普通的方法,并没有真正的启动一个线程,会先把run方法执行完成之后,再往下执行
      // 说明:当main线程启动一个子线程Thread-0,主线程不会阻塞,会继续执行
       //  public synchronized void start() {
		// start0();
		// 真正实现多线程效果的是start0(),是native方法
		// start()方法调用start0()方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态。具体什么时候执行,取决于CPU,由CPU统一调度
        // }
		for(int i =0;i<10;i++) {
		sout("321");
		// 让主线程休眠
		 Thread.sleep(1000);
}
    }
}

class Cat extends Thread {
    @Override
    public void run() {
        int i = 0;
        while (i < 100) {
            System.out.println(i + "123");
            i++;
            // 让线程休眠1s
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

应用2-实现Runnable接口
说明
1.java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread方法来创建线程显然不可能了。
2.java设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程
这里使用了设计模式【代理模式】

public class Dog implements Runnable{
    public static void main(String[] args) {
        Dog dog = new Dog();
        // dog.start;这里不能调用start
        Thread thread = new Thread(dog);
        thread.start();
    }
    @Override
    public void run() {
        System.out.println("123");
    }
}

继承Thread和实现Runnable的区别

  1. 从JAVA的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,从jdk帮助文档我们可以看到Thread类本身就实现了Runnable接口
  2. 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制。建议使用Rnnable
    在这里插入图片描述

三,线程终止

1.当线程完成任务后,会自动退出
2.还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式

package stop;

public class ThreadExit {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();
        // 如果希望main线程去终止T线程    通知方式
        Thread.sleep(10000);
        t.stopE(false);
    }
}

class T extends Thread {
    // 设置一个控制变量
    private boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {

            }
            System.out.println("运行中");
        }
    }

    public void stopE(boolean flag) {
        this.flag = flag;
    }
}

四,线程常用方法

常用方法第一组

 - setName // 设置线程名称,使之与参数name相同
 - getName // 返回该线程的名称
 - start // 使线程开始执行;Java虚拟机底层调用该线程的start0方法
 - run // 调用线程对象run方法
 - setPriority // 更改线程的优先级
 - getPro]iority // 获取线程优先级
 - sleep // 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
 - interrupt // 中断线程(不是停止)

注意
1.start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程
2.线程优先级的范围
在这里插入图片描述

3.interrupt,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程
4.sleep:线程的静态方法,使当前线程休眠

常用方法第二组
1.yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不稳定,所以也不一定礼让成功(谁让执行谁的yield)
2.join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的人物(谁要插进来,执行谁的join)
案例:创建一个子线程,每隔1s输出hello,输出20次,主线程每隔1s,输出hi,输出20次。要求:两个线程同时执行,当主线程输出5次后,就让子线程运行完毕,主线程再继续

public class M2 {
    public static void main(String[] args) throws InterruptedException {
        T1 t1 = new T1();
        Thread thread = new Thread(t1);
        thread.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("main");
            if (i == 5) {
                thread.join();// 让子线程先执行完毕
            }
        }

    }
}


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

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

/*
main
main
main
main
main
main
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
T1
main
main
main
main
main
main
main
main
main
main
main
main
main
main*/

五,用户线程和守护线程

1.用户线程: 也叫工作线程,当线程的任务执行完或通知方式结束
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
3.常见的守护线程:垃圾回收机制

package method;

public class TH3 {
    public static void main(String[] args) throws InterruptedException {
        T01 t01 = new T01();
        Thread thread = new Thread(t01);
        // 如果我们希望当main结束后,子线程可以自动结束
        // 只需将子线程设为守护线程  先设置,再启动
        thread.setDaemon(true);
        thread.start();

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


class T01 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; ; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("T1");
        }
    }
}

六,线程的生命周期

JDK中用Thread.State枚举表示了线程的几种状态

public static enum Thread.State extends Enum<Thread.State>

线程状态,线程可以处于以下状态之一

  • NEW 尚未启动的线程处于此状态
  • RUNNABLE 在Java虚拟机中执行的线程处于此状态
  • BLOCKED 被阻塞等待监视器锁定的线程处于此状态
  • WATTING 正在等待另一个线程执行特定动作的线程处于此状态
  • TIMED WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
  • TERMINATED 已退出的线程处于此状态

在这里插入图片描述

七,Synchronized

线程同步机制
1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就是用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整新=性
2.也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作

同步具体方法-Synchronized

1.同步代码块
synchronized(对象) {
// 得到对象的锁,才能操作同步代码
// 需要被同步的代码
}

2.synchronized还可以放在方法声明中,表示整个方法为同步方法
public synchronized void m(String name) {
// 需要被同步的代码
}

3.如何理解:
就好像 某小伙伴上厕所前先把门关上(上锁),完事后再出来(解锁),那么其它小伙伴就可以在使用厕所了

  // 说明
    // private synchronized void sell(){} 就是一个同步方法
    // 这时 锁 在 this对象
    // 也可以在代码块上加锁,同步代码块
 private synchronized void sell() {
        if (num <= 0) {
            System.out.println("售票结束");
            this.loop = false;
            return;
        }
        // 休眠50ms
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("窗口" + Thread.currentThread().getName() + "卖了1张" + "剩余" + --num);
    }


 private void sell() {
        synchronized (this) {
        if (num <= 0) {
            System.out.println("售票结束");
            this.loop = false;
            return;
        }
            // 休眠50ms
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口" + Thread.currentThread().getName() + "卖了1张" + "剩余" + --num);

        }
       }

8,互斥锁

基本介绍

1.Java在Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
2.每个对象都对应于一个可称为“互斥锁“的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
3.关键字synchromize来与对象的互斥所联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
4.同步的局限性:导致程序的执行效率降低
5.同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
6.同步方法(静态的)的锁为当前类本身。
class Sell02 implements Runnable {
    /**
     * 同步方法(静态的)的锁为当前类本身
     * 1.public synchronized static void m1(){}锁是加在Sell02.class
     * 2.如果在静态方法中,实现一个同步代码块,用类名.class
     */
    public synchronized static void m1(){

    }

    public static void m2(){
        // synchronized (this)报错
        synchronized (Sell02.class){
            System.out.println("m2");
        }
    }
}

注意事项和细节

1.同步方法如果没有使用static修饰:默认锁对象为this
2.如果方法使用static修饰,默认锁对象:当前类.class
3.实现的落地步骤

 - 需要线分析上锁的代码
 - 选择同步代码块或同步方法
 - 要求多个线程的锁对象为同一个即可 比如考虑new了多个对象,锁的是不是同一个对象

九,线程的死锁

基本介绍
多个线程占用了对方的锁资源,但不肯相让,导致了死锁,在编程一定要避免死锁的发生

应用案例
妈妈:你先写作业,才让你玩手机
小明:你先让我玩手机,我才写作业

十,释放锁

下面操作会释放锁
1.当前线程的同步方法,同步代码块执行结束
案例:上厕所,完事出来
2.当前线程在同步代码块,同步方法中遇到breakreturn
案例:没有正常的完事,经理叫他修改bug,不得已出来

3.当前线程在同步代码块,同步方法中出现了未处理的ErrorException,导致异常结束
案例:没有正常的完事,发现忘带纸,不得已出来
4.当前线程在同步代码块,同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁
案例:没有正常完事,觉得需要酝酿下,所以出来等会再进去
下面操作不会释放锁
1.线程执行同步代码块或同步方法时,程序调用Thread.sleep(),Thread.yield()方法暂停当前线程的执行,不会释放锁
案例:上厕所,太困了,在坑位上眯了一会

2.线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁
提示:应尽量避免使用suspend()resume()来控制线程,方法不再推荐使用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

华华华华华12138

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值