java多线程的学习总结

首先看一张关于多线程的图

该图转载于http://blog.youkuaiyun.com/evankaka
这里写图片描述
看完之后会对多线程的流程有个大概的理解,稍后的学习也可以重新查看该图,进行加深理解

关于线程和进程的概念

进程:将程序加载到内存中,系统为它分配资源后才能执行的程序称之为进程。(是系统进行资源分配和调度的一个独立单位,每个进程都有独立的地址空间)
线程:是进程的一个实体(是CPU调度和分配的基本单位,比进程更小的能独立运行的基本单位.)
多进程是指电脑能同时运行多个程序
多线程是指电脑在一个程序中能同时运行顺序流

线程和进程的状态和相关转换

线程和进程都有五个状态:
创建,就绪,运行,堵塞,终止

  1. 新建状态:新创建的一个线程对象
  2. 就绪状态:线程创建后,其他线程调用该线程对象的start()方法。将该线程放入可运行线程池中,等待CPU调度。(线程调用调用start()方法事实上不是把线程转换为运行状态,而是从新建状态转换为就绪状态)
  3. 运行状态:就绪状态中的线程获得CPU调度,开始该线程程序执行
  4. 阻塞状态:是线程因为某些原因放弃了CPU使用权,暂时停止运行。只用当程序重新进入就绪状态,才有机会转为运行状态。阻塞状态分为三种:
    (1) 等待阻塞:指运行的线程执行了wait()方法,当前线程会放弃对该对象的锁,JVM将该线程放入等待池中(wait会释放该线程所持有的锁)
    (2) 同步阻塞:指运行的线程在获取对象的同步锁时,该锁被其他线程占用,JVM会将该线程放入锁池
    (3)其他阻塞:运行的线程执行sleep()或join()方法,进入阻塞状态,等待CPU调度,线程重新进入就绪状态(sleep不会释放该线程所持有的锁)
  5. 死亡状态:该线程执行完了,或者调用了某些方法退出了run()方法,结束了生命周期

一 如何实现多线程

实现多线程有两种方式,一种是继承Thread类,一种是实现Runnable接口。推荐使用实现Runable接口的方式

继承Thread类的方式


class Thread1 extends Thread{
    private String name;

    public Thread1(String name){
        this.name=name;
    }

    @Override
    public void run() {
        System.out.println(name+"开始运行:");
        for(int i=0;i<5;i++){
            System.out.println(name+"正在运行:"+i);
        }

    }

}

public class HelloThread {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"线程开始运行:");
        Thread t1 = new Thread(new Thread1("线程A"));
        Thread t2 = new Thread(new Thread1("线程B"));
        t1.start();
        t2.start();
    }
}
运行结果如下
main线程开始运行:
线程A开始运行:
线程B开始运行:
线程A正在运行:0
线程B正在运行:0
线程A正在运行:1
线程A正在运行:2
线程A正在运行:3
线程A正在运行:4
线程B正在运行:1
线程B正在运行:2
线程B正在运行:3
线程B正在运行:4
在运行一下,得出新的结果
main线程开始运行:
线程A开始运行:
线程B开始运行:
线程A正在运行:0
线程B正在运行:0
线程A正在运行:1
线程B正在运行:1
线程A正在运行:2
线程B正在运行:2
线程A正在运行:3
线程A正在运行:4
线程B正在运行:3
线程B正在运行:4

实现Runnable接口的方式

class Thread1 implements Runnable{

    private String name;

    public Thread1(String name){
        this.name=name;
    }

    @Override
    public void run() {
        System.out.println(name+"开始运行:");
        for(int i=0;i<5;i++){
            System.out.println(name+"正在运行:"+i);
        }
    }

}
public class HelloThread {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"线程开始运行:");
        Thread t1 = new Thread(new Thread1("线程A"));
        Thread t2 = new Thread(new Thread1("线程B"));
        t1.start();
        t2.start();
    }
}
运行结果如下
main线程开始运行:
线程A开始运行:
线程B开始运行:
线程A正在运行:0
线程B正在运行:0
线程A正在运行:1
线程B正在运行:1
线程A正在运行:2
线程B正在运行:2
线程A正在运行:3
线程B正在运行:3
线程A正在运行:4
线程B正在运行:4
再运行一下
main线程开始运行:
线程A开始运行:
线程A正在运行:0
线程A正在运行:1
线程A正在运行:2
线程A正在运行:3
线程A正在运行:4
线程B开始运行:
线程B正在运行:0
线程B正在运行:1
线程B正在运行:2
线程B正在运行:3
线程B正在运行:4
不管是哪种方式,都是通过线程start()方法,将线程置为就绪状态,然后会调用run()方法内的程序块来进行操作。 Thread类事实上也是实现了Runnable接口,我们可以看下Thread类的截图 这里写图片描述 在这里,我们可以有1个小技巧
在线程类中定义一个私有领域线程名name的属性,其他线程调用时,通过线程构造器传入,那么该线程将会有指定的线程名

二 线程的调度

java线程存在优先级,优先级较高的线程得到CPU资源更多,CPU优先执行较高的线程对象的任务块
java线程优先级用整数表示,取值范围1~10,Thread类有三个静态常量代表该线程优先级

    public final static int MIN_PRIORITY = 1;
//线程具有的最低优先级
    public final static int NORM_PRIORITY = 5;
//线程具有的默认优先级
    public final static int MAX_PRIORITY = 10;
//线程具有的最高优先级

Thread类还有两个方法类设置和获取线程的优先级

 public final void setPriority(int newPriority)
 public final int getPriority() 

三 线程阻塞状态转化相关概念

  1. 线程睡眠
    Thread类的sleep(long millis)方法,将运行状态的线程转为其他阻塞状态。millis参数以毫秒为单位,设置睡眠时间。当睡眠结束,转为就绪状态。注意:sleep状态不解锁

  2. 线程等待
    Object类的wait()方法,导致该线程放弃对该对象的锁进行等待阻塞状态,置入等待池。只有其他线程调用该对象notify()方法,或者调用notifyAll()方法才能唤醒,进入锁池(同步阻塞),等待拿到该对象的锁后,才能重新回到就绪状态

  3. 线程让步
    Thread.yield()方法,暂停当前正在执行的线程对象,放入就绪状态,从就绪状态中重新调出相同或更高优先级的线程

  4. 线程加入
    join()方法,在当前线程中调用另一个线程的join()方法,使当前前程进入其他阻塞状态,直到另一个线程运行结束,当前线程才从其他阻塞状态转为就绪状态

  5. 线程唤醒
    Object类中的notify()方法,唤醒在此对象中等待池内的单个线程,将其从等待阻塞状态转为锁池状态,等到拿到对应的锁后转为就绪状态。
    Object类中的notifyAll()方法,唤醒此对象中等待池内的所有线程。

四 线程相关的常用函数说明和代码示例

  1. sleep()
    sleep()方法在Thread中的代码块如下
 public static native void sleep(long millis) throws InterruptedException;

它允许传入一个毫秒为单位的参数,代表暂停的时间。同时它会抛出一个名为InterruptedException的异常,要求你在使用它的时候捕捉该异常,或者继续抛出该异常
作用:
让当前线程进入其他阻塞(–线程睡眠)状态,让就绪状态的线程有运行机会,该机会不考虑线程优先级。同时,该线程不会解除该线程占用的对象锁,所以其后其他线程如果用到该线程所占用的对象锁时,将无法使用该对象,只能进入同步阻塞状态,进入锁池
示例:

class Thread1 implements Runnable{

    private String name;

    public Thread1(String name){
        this.name=name;
    }

    @Override
    public void run() {
        System.out.println(name+"开始运行:");
        for(int i=0;i<3;i++){
            System.out.println(name+"正在运行:"+i);
            try {
                System.out.println(name+"进入睡眠状态:");
                Thread.currentThread().sleep(1*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name+"结束睡眠状态:");
        }
        System.out.println(name+"进入死亡状态:");
    }

}
public class HelloThread {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName()+"线程开始运行:");
        Thread t1 = new Thread(new Thread1("线程A"));
        Thread t2 = new Thread(new Thread1("线程B"));
        t1.start();
        t2.start();
    }
}
输出结果如下
main线程开始运行:
线程A开始运行:
线程B开始运行:
线程A正在运行:0
线程B正在运行:0
线程B进入睡眠状态:
线程A进入睡眠状态:
线程B结束睡眠状态:
线程B正在运行:1
线程B进入睡眠状态:
线程A结束睡眠状态:
线程A正在运行:1
线程A进入睡眠状态:
线程B结束睡眠状态:
线程B正在运行:2
线程A结束睡眠状态:
线程A正在运行:2
线程A进入睡眠状态:
线程B进入睡眠状态:
线程B结束睡眠状态:
线程B进入死亡状态:
线程A结束睡眠状态:
线程A进入死亡状态:
从上面我们可以看到
线程A开始运行一个环节后,进入休眠状态。
然后CPU会从就绪状态中调一个线程B,线程B会开始一个环节后进入休眠状态。
在B线程运行完后,就绪状态中没有线程调用。
所以会等到A线程结束后(时间为休眠时间),回到就绪状态,结束了休眠状态。
CPU在继续从就绪状态中调A线程的下一个环节。
如此循环往复
看起来很有规律是吗?因为只有两个线程的缘故,所以两者是循环的,我们加上线程C后,就会得出,它是从就绪状态中调用一个线程,而不是直接承接上一个线程的睡眠状态 下面是新程序的输出结果
main线程开始运行:
线程A开始运行:
线程B开始运行:
线程B正在运行:0
线程B进入睡眠状态:
线程A正在运行:0
线程A进入睡眠状态:
线程C开始运行:
线程C正在运行:0
线程C进入睡眠状态:
线程A结束睡眠状态:
线程A正在运行:1
线程A进入睡眠状态:
线程C结束睡眠状态:
线程B结束睡眠状态:
线程B正在运行:1
线程B进入睡眠状态:
线程C正在运行:1
线程C进入睡眠状态:
线程B结束睡眠状态:
线程B正在运行:2
线程B进入睡眠状态:
线程A结束睡眠状态:
线程A正在运行:2
线程C结束睡眠状态:
线程A进入睡眠状态:
线程C正在运行:2
线程C进入睡眠状态:
线程B结束睡眠状态:
线程B进入死亡状态:
线程A结束睡眠状态:
线程A进入死亡状态:
线程C结束睡眠状态:
线程C进入死亡状态:

我们可以重新看到线程的无序状态,CPU每次调用进程中的线程,都可能得出的线程执行顺序是不同

2.object对象的wait()方法
object对象的wait(),notify()和notifyAll()都需要放在同步代码块synchronized(object){}当中,object表示当前线程锁定的对象
作用:线程在获得对象锁后,主动释放对象锁,同时自身线程进入等待状态。只有当其他线程调用notify()或者notifyAll()方法时,才会唤醒

class Thread1 implements Runnable {
    private Object o1;
    private String name;
    public Thread1(String name,Object o) {
        this.name = name;
        this.o1=o;
    }

    @Override
    public void run() {
        System.out.println(name + "开始运行:");
        synchronized(o1){
            try {
                System.out.println(name + "进入等待状态:释放了对o1的锁的控制");
                o1.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + "结束等待状态,进入锁池,等待对象解锁后进入就绪状态");
        }
        for(int i=0;i<3;i++){
            System.out.println(name+"正在运行:"+i);
        }
        System.out.println(name + "进入死亡状态:");
    }

}
class Thread2 implements Runnable {
    private Object o1;
    private String name;
    public Thread2(String name,Object o) {
        this.name = name;
        this.o1=o;
    }

    @Override
    public void run() {
        System.out.println(name + "开始运行:");
        //让唤醒其他线程的线程先休眠,让其他线程进入等待池后再唤醒,避免该线程进行唤醒行为后,
        //再有其他线程进入等待池后,该线程无法将其唤醒的情况发生
        try {
            Thread.currentThread().sleep(1*1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        synchronized(o1){
            o1.notifyAll();
            System.out.println(name +"唤醒了o1对象等待池中的所有线程");
        }
    }

}
public class HelloThread {
    private static Object o1 = new Object();
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName() + "线程开始运行:");
        Thread t1 = new Thread(new Thread1("线程A",o1));
        Thread t2 = new Thread(new Thread1("线程B",o1));
        Thread t3 = new Thread(new Thread2("线程C",o1));
        t1.start();
        t2.start();
        t3.start();
    }
}
运行结果:
main线程开始运行:
线程A开始运行:
线程C开始运行:
线程B开始运行:
线程A进入等待状态:释放了对o1的锁的控制
线程B进入等待状态:释放了对o1的锁的控制
线程C唤醒了o1对象等待池中的所有线程
线程B结束等待状态,进入锁池,等待对象解锁后进入就绪状态
线程B正在运行:0
线程A结束等待状态,进入锁池,等待对象解锁后进入就绪状态
线程A正在运行:0
线程B正在运行:1
线程B正在运行:2
线程B进入死亡状态:
线程A正在运行:1
线程A正在运行:2
线程A进入死亡状态:

3.join()方法
这是Thread类关于join方法的代码块,它将抛出InterruptedException 异常

   public final void join() throws InterruptedException {

作用:在A线程中调用B线程的join()方法,那么A线程将进入其他阻塞状态,等待B线程终止运算才能继续运行
应用场景:主线程中生成子线程,子线程如果需要大量运算,那么主线程极有可能在子线程结束前已经结束。如果主线程中需要用到子线程运算后的结果,那么需要你在主线程中调用子线程的join()方法,让主线程进入阻塞状态,等到子线程运算完了,主线程在继续执行接下来的步骤

代码示例

class Thread1 implements Runnable {
    private String name;
    public Thread1(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println(name + "开始运行:");
        for(int i=0;i<3;i++){
            System.out.println(name+"正在运行:"+i);
        }
        System.out.println(name + "结束运算:");
    }

}
public class HelloThread {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName() + "线程开始运行:");
        Thread t1 = new Thread(new Thread1("线程A"));
        Thread t2 = new Thread(new Thread1("线程B"));
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "线程结束运行:");

    }
}
运行结果
main线程开始运行:
线程A开始运行:
线程B开始运行:
线程A正在运行:0
线程B正在运行:0
线程A正在运行:1
线程B正在运行:1
线程A正在运行:2
线程B正在运行:2
线程A结束运算:
线程B结束运算:
main线程结束运行:
如果将join方法注释掉,那么运行结果会如下
main线程开始运行:
main线程结束运行:
线程B开始运行:
线程B正在运行:0
线程B正在运行:1
线程B正在运行:2
线程B结束运算:
线程A开始运行:
线程A正在运行:0
线程A正在运行:1
线程A正在运行:2
线程A结束运算:

4.yield()方法
这是Thread类关于yield方法的代码块,

    public static native void yield();

作用:暂停当前正在运行的线程,让它回到就绪状态,在从就绪状态中相同优先级甚至更高优先级的线程得到运行机会
缺陷:有可能当前线程回到就绪状态后,还没给其他相同优先级运行机会,自己又抢得了CPU的调度,进行接下来的运算

class Thread1 implements Runnable {
    private String name;
    public Thread1(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        for(int i=0;i<50;i++){
            System.out.println(name+"正在运行:"+i);
            if(i==40){
                Thread.currentThread().yield();
                System.out.println(name+"让步了");
            }
        }
    }

}
public class HelloThread {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Thread1("线程A"));
        Thread t2 = new Thread(new Thread1("线程B"));
        t1.start();
        t2.start();
    }
}
运行结果有两种, 一种是线程让步后,让另一个线程得到了运行机会
部分结果

线程B正在运行:39
线程A正在运行:37
线程B正在运行:40
线程A正在运行:38
线程B让步了
线程A正在运行:39
线程B正在运行:41
线程A正在运行:40
线程B正在运行:42
线程B正在运行:43
线程A让步了
线程B正在运行:44
线程A正在运行:41
线程B正在运行:45
线程A正在运行:42
一种是线程让步后,结果回到就绪状态后自己又得到CPU调度,再次运行
部分结果
线程B正在运行:30
线程A正在运行:40
线程B正在运行:31
线程B正在运行:32
线程B正在运行:33
线程B正在运行:34
线程B正在运行:35
线程B正在运行:36
线程B正在运行:37
线程B正在运行:38
线程B正在运行:39
线程B正在运行:40
线程A让步了
线程A正在运行:41
线程A正在运行:42
线程A正在运行:43
线程B让步了
线程B正在运行:41
线程B正在运行:42
线程B正在运行:43
线程B正在运行:44
线程B正在运行:45

五 关于线程函数的不同之点

  1. sleep()和wait()
    (1).sleep()方法执行后,线程进入其他阻塞(睡眠)状态,期间对对象的锁并不会解开
    wait()方法执行后,线程进入等待阻塞状态,期间对对象的锁会解开
    (2)线程阻塞后,sleep()方法会在指定时间结束后转为就绪状态
    wait()方法必须在经过其他线程调用notify()或notifyAll()方法唤醒后,转到锁池,等到得到对象的锁后,才会转为就绪状态
    (3)wait()是Object类的方法,和notify()和notifyAll()需要在synchronized(object)同步代码块中才能使用,是根据对象的方式
    sleep()是Thread类的方法,任何时候都可以用,是根据线程的方式
  2. sleep()和yield()
    (1)sleep()在指定的时间内不可能再次获得执行机会
    yield()在让步后,可能会再次获得执行机会
    (2)sleep()不考虑优先级问题
    yield()会考虑优先级问题
    (3)sleep()在运行后,线程进入其他阻塞(睡眠)状态,等待时间结束才会转为就绪状态
    yield()会直接转为就绪状态

结尾:这是我学习过程的一些总结,借鉴其他帖子,借鉴其他书后写出来的,例子可能不完美,语句也可能有误。主要用于以后面试初步复习的一个路径,可能接下来会重新修缮下,添加关于线程的其他知识点

【完美复现】面向配电网韧性提升的移动储能预布局与动态调度策略【IEEE33节点】(Matlab代码实现)内容概要:本文介绍了基于IEEE33节点的配电网韧性提升方法,重点研究了移动储能系统的预布局与动态调度策略。通过Matlab代码实现,提出了一种结合预配置和动态调度的两阶段优化模型,旨在应对电网故障或极端事件时快速恢复供电能力。文中采用了多种智能优化算法(如PSO、MPSO、TACPSO、SOA、GA等)进行对比分析,验证所提策略的有效性和优越性。研究不仅关注移动储能单元的初始部署位置,还深入探讨其在故障发生后的动态路径规划与电力支援过程,从而全面提升配电网的韧性水平。; 适合人群:具备电力系统基础知识和Matlab编程能力的研究生、科研人员及从事智能电网、能源系统优化等相关领域的工程技术人员。; 使用场景及目标:①用于科研复现,特别是IEEE顶刊或SCI一区论文中关于配电网韧性、应急电源调度的研究;②支撑电力系统在灾害或故障条件下的恢复力优化设计,提升实际电网应对突发事件的能力;③为移动储能系统在智能配电网中的应用提供理论依据和技术支持。; 阅读建议:建议读者结合提供的Matlab代码逐模块分析,重点关注目标函数建模、约束条件设置以及智能算法的实现细节。同时推荐参考文中提及的MPS预配置与动态调度上下两部分,系统掌握完整的技术路线,并可通过替换不同算法或测试系统进一步拓展研究。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值