Java—多线程相关知识

目录

前言

        本文只是涉及到多线程的基本知识,未对线程池,并发做研究。
        相关参博客和视频:
https://www.jianshu.com/p/512ab59ee584
https://www.cnblogs.com/hongmoshui/p/11109982.html
https://www.cnblogs.com/yangyongjie/p/11024712.html
https://blog.csdn.net/qq_36711757/article/details/82384356
https://www.bilibili.com/video/BV1V4411p7EF?from=search&seid=14826241534626431382

一. 进程与线程的简单理解

        一个程序就是一个进程,系统运行一个程序即使一个进程从创建,运行到消亡的过程。而一个程序中的多个任务则被称为线程。
        进程表示资源分配的基本单位,线程是进程执行运算的最小单位,也是调度运行的基本单位。

二. 线程调度

        分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
        抢占式调度:优先级高的线程先使用CPU,如果优先级相同则随机选择一个。

三. 创建线程的方式

        继承Thread类创建线程类
        通过Runnable接口创建线程类
        通过Callable和Future创建线程(不常用,少见)。
        一般就是通过继承Thread类或者调用Runnable接口来重写run()方法实现线程,调用start()方法启动线程。

四. 用Runnable还是Thread?

        一般建议使用Runnable接口,因为java是单继承的,实现Runnable接口可以避免这个单继承的局限性

五. start()方法和run()方法的区别

        这也是面试常见问题,涉及到对java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法时,只会在原来的线程中调用,没有开启新的线程,只有shart()方法才会启动新线程。

六. Thread的实现涉及到哪种设计模式

        源代码中Thread的实现是静态代理。静态代理就是比如,你结婚,你会和新娘宣读誓言,但是除了这,还有布置现场,主持流程这些都是由婚庆公司来做,婚庆公司就是代理,来帮你处理结婚的事情。
        而线程也是类似的,比如我们自己写个抢票的线程,抢票这个类会实现Runnable接口,同时Thread类也是实现Runnable接口的,然后回调用Thread.start()来帮忙抢票,这个Thread就是代理,相当于婚庆公司。具体理解可以在以后写线程代码时细细体会。

七. 线程状态

        1,New初始状态:线程对象被创建后,就进入了新建状。例如,Thread thread=new Thread()。
        2,Runnable就绪状态:线程对象被创建后,该对象的start()方法被调用了,该线程启动,处于就绪状态,随时可被CPU调度执行。并不是调用start()方法后马上线程就运行了,要等cpu来调用了,才会开始运行。
        3,Running运行状态:先后获取CPU权限进行执行,需要注意的是,线程只能从就绪状态进入到与性能状态。
        4,阻塞状态:阻塞状态时线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到阻塞状态。阻塞的情况分三种:
               1) 等待阻塞—通过调用线程wait()方法,让线程等待某工作的完成。
               2)同步阻塞—线程在获取synchronized同步锁失败(因为锁被其他线程所占用),它会进入阻塞状态。
               3)其他阻塞—通过调用线程sleep()或者join()或发出了I/O请求时,线程会进入阻塞状态。当sleep()状态超时,join()等待线程中止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
        5,Dead死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
在这里插入图片描述
在这里插入图片描述

八. 线程相关方法
8.1 相关方法

        sleep()方法:在指定的毫秒数内让当前正在执行的线程休眠。线程休眠不会释放锁。抱着锁睡觉
        join()方法:简单理解就是插队,vip,先完成这个线程,再执行其他线程,其他线程阻塞。
        setPriority:设置线程优先级,优先级高的,被cpu调用的可能性增大(只是可能性大)。
        yield()方法:线程礼让,让当先正在执行的线程暂停,但不阻塞,将线程从运行状态转为就绪状态,让cpu重新调用。礼让也是不一定成功的,看cpu心情。
        线程停止方法:不推荐使用JDK提供的stop()、destory()方法,推荐让线程自己停止下来。一般使用一个标志位来帮忙中止线程(while(flag)…)。
        wait()方法,notify()方法:wait()方法的作用是让需要锁的线程等待,直到其他线程调用notify()或着notifyAll()唤醒方法。与sleep()方法的不同是,sleep()方法睡眠时,保持对象锁,仍然占有该锁,而wait()睡眠时,释放对象锁。

8.2 例子
/* 

经典面试题:建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。

这个问题用Object的wait(),notify(),sleep()就可以很方便的解决

*/

class Thread1 implements  Runnable {

    private String name;
    private Object prev;
    private Object self;
    
    private Thread1(String name, Object prev, Object self) {
        this.name = name;
        this.prev = prev;
        this.self = self;
    }
    @Override

    public void run() {
        int count = 10;
        while (count > 0) {
            synchronized (prev) {
                synchronized (self) {
                    System.out.print(name);
                    count--;
                    self.notify();
                }
                try {
                    prev.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args) throws Exception {
        Object a = new Object();
        Object b = new Object();
        Object c = new Object();
        Thread1 pa = new Thread1("A", c, a);
        Thread1 pb = new Thread1("B", a, b);
        Thread1 pc = new Thread1("C", b, c);

        new Thread(pa).start();
        Thread.sleep(100);  //确保按顺序A、B、C执行
        new Thread(pb).start();
        Thread.sleep(100);
        new Thread(pc).start();
        Thread.sleep(100);
    }
}

/*结果:ABCABCABCABCABCABCABCABCABCABC*/
九. 线程安全与线程同步

        当我们使用多个线程访问同一资源的时候,且多个线程对资源有写的操作,那么就容易出现线程安全问题。在Java中为解决线程提供了同步机制。
        线程安全的核心理念是“要么只读,要么加锁”
        计算机的线程同步,是指线程之间按照某种机制协调先后执行次序,即当有一个线程再对内存操作时,其他线程都不可以对这个内存操作,一直等待直到该线程完成操作,其他线程才能对该内存进行操作。
        实现线程同步的方式有很多,比如同步方法,锁,阻塞队列等。

十. 线程同步的方法
10.1 同步代码块

        synchronized(['sɪŋkrənaɪzd] 同步)关键字可以用于方法中的某个区块,表示只对这个区块的资源实行互斥访问。

synchronized(Obj){
  需要同步操作的代码
}
//这个Obj可以是任何对象,但是推荐共享资源作为Obj

        可以理解为给对象上了个锁。多个线程对象,要使用同一把锁,任何时候,最多允许一个线程拥有同步锁,谁拿到锁谁就进入代码块,其他的线程只能在外等待。
        在多线称竞争情况下,加锁,释放锁会导致比较多的上下文切换和调度演示等问题,性能下降。

10.2 同步方法

        使用synchronized修饰的方法就叫做同步方法,保证A线程在执行的时候,其他线程只能在外面等着。
        方法里面又需要修改的内容才需要锁,锁的太多也容易浪费资源。

10.3 Lock锁

        JDK5.0后,提供了更加强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。

10.3 synchronized与Lock锁对比

        Lock是显式锁,需要手动的开启和关闭,synchronized是隐式锁,出了作用域就自动释放。
        Lock只有代码块锁,synchronized有代码块和方法锁。
        使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性。
        优先使用顺序:Lock> 同步代码块 >同步方法。

十一. 死锁

        多个线程同时各自占有一些资源,并且相互等待着其他线程占有的资源才能允许,而导致两个或者多个线程都在等待着对方释放资源,都停止执行的情况。
        那么如何避免死锁呢:死锁的产生有四个必要条件:互斥条件,请求与保持条件,不剥夺条件,循环等待条件。只要破环任何一个就可以。比如我们可以指定获取锁的顺序。

十二. 线程通信

        具体的没搞太明白,涉及到了线程池,生产者消费者模式(这不属于设计模式的一种,只是一种问题类型的描述)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值