Java-关于多线程

1.概念

1.1 多线程里的各种名词术语是什么意思?

名词释义
进程完整的应用程序,可包含多个线程
线程应用程序的一部分,一般分为主(运行)线程和其它功能线程(时间线程,垃圾回收线
串行这个进/线程执行时只能一步步来,不能跳跃执行。(吃完饭再看电影)
并行同时执行多个进/线程(看着电影吃饭)
并发短时间内执行多个线/进程,但切换速度很快,人类看来就是同时执行多个应用程序(重复->低头吃一口饭然后抬头看电影)
-*如电脑中同时打开多个应用,一般是并发。
单线程串行 (一长串任务列表)
多线程并行/并发(多个短任务列表)
用户线程一个执行任务的线程
守护线程有用户线程则一直运行,无则自动结束(如垃圾回收线程)

1.2 并行和并发的区别?

并行是同时执行,而并发只是一种现象,看起来同时执行,实际上是快速切换。
且并行与否由计算机判断,无法操控,而代码里的多线程实际上全部都是并发。

1.3 为什么需要多线程?

提高程序执行效率

1.4 run和start的区别?

名称runstart
任务运行代码开辟一个新的栈空间
运行空间分支线程主线程(main)
销毁任务完成任务完成

*start开辟空间后,jvm来调度run方法

1.5 interrupt、join、sleep和yield方法

名称sleepinterruptyieldjoin
作用(睡眠)阻塞线程中断线程当前状态让步合并线程
回到阻塞状态就绪/阻塞状态就绪状态当前阻塞->其它运行->当前运行
解释使线程一段时间内不执行使线程取消睡眠状态或中断运行使运行中的线程让给其它线程执行使指定线程合并到当前线程,阻塞当前线程并执行指定线程,执行完成后继续执行当前线程
细项抛出InterruptedException异常,阻塞后会让步给低优先级的线程只会让步给相同或更高优先级的线程多线程按单线程的运行方式去运行

1.6 常见的线程调度模型

抢占式调度模型:
  优先级越高,抢占cpu时间片的概率越高
  java采用的就是这种模型
均分式调度模型:
  平均分配cpu时间片,每个线程占用时间片时间长度一致。

2.运行时

2.1 资源共享情况

线程之间 推内存和方法区共享 栈内存独立
*栈内存-程序运行的主要活动空间,为方法的执行过程(包括线程)分配空间
*堆内存-用于存储对象及对象属性
*方法区-用于存储代码 (如保存函数与类的相关数据以重复调用)

2.2 线程的生命周期

情况1->新建->start->就绪->run->运行->run结束->死亡
情况1.2->运行->阻塞->就绪

*新建状态(只新建了对象,尚未调用start方法分配栈空间,无法运行)
*就绪/可运行状态(已分配空间,可运行,由jvm决定是否运行)
*运行状态(执行代码,若阻塞,解除后会继续执行剩余部分)
*阻塞时线程放弃执行权(如Scanner获取输入,此时主线程阻塞,垃圾回收线程仍然执行)
*死亡/销毁状态(已销毁)

2.3 如何正确的终止线程?

使用boolean来判断是继续执行还是保存数据后退出

若使用stop方法,容易导致丢包


public class test {
    public static void main(String[] args) {
        MyThread m = new MyThread();
        Thread t = new Thread(m);
        t.start();
        try {
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        m.active = false;//关闭线程
    }
}
class MyThread implements Runnable {
    boolean active = true;
    @Override
    public void run() {
        String name=Thread.currentThread().getName();
        for (int i = 0; i < 99; i++) {
            if (active) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(name+" "+i);
            } else {
                //save data
                System.out.println( name+ " close......");
                return;
            }
        }
    }
}

3.如何实现多线程?

  1. 编写一个继承Thread的类,重写run方法(新建对象并调用start方法)
  2. 编写一个实现Runnable接口的类,实现run方法(新建对象并调用start方法)(可用匿名内部类)
  3. FutureTask方式,实现Callable接口(JDK8+)

    3.1 FutureTask方式实例
FutureTask t=new FutureTask(new Callable(){
    @Override
    public void call() throws Exception{
        return 1;
    }
});
Int i=t.get();//可能导致当前线程阻塞

*推荐方法2,java只能单继承,所以1具有局限性

*方法3实现的线程可以获取线程返回值

4.多线程并发环境,数据的安全问题

4.1什么时候会存在安全问题?

1. 多线程并发

2. 共享数据

3. 修改共享数据

如银行账户管理系统

卡1余额:1000

用户a与b同时查询与取出1000元

卡1余额:1000->0->0

用户a取出时用户b也取出了,此时网络并未返回数据,余额还是1000

所以a与b同时取出1000元,共2000元,银行亏损1000元。

4.2如何解决线程安全问题?

1. 尽量使用局部变量来代替实例变量和静态变量

2. 如果必须是实例变量,考虑创建多个对象,这些实例变量的内存就不共享了

3. 若无法使用局部变量,也不能创建多个对象,使用线程同步机制,线程必须排队执行(会影响部分效率)

synchronized(/*共享对象*/){
    //代码
}
void synchronized dosomething(){
    //代码
}

若synchronized关键词出现在方法,对象锁,对象为this。

若synchronized关键词出现在静态方法,为类锁(所有实例共享锁)。

*只有局部变量是永远安全的(因为永远不共享,在方法内运行)

线程同步涉及到编程模型

异步编程模型: 线程之间互不相关,各自执行(效率较高)

同步编程模型: 线程之间同时只能执行一个(效率较低)

4.3不当的使用synchronized会造成死锁

    Object o1,o2;
    synchronized(o1){
        synchronized(02){}
    }
    synchronized(o2){
        synchronized(01){}
    }

线程a执行上方代码块时锁住o1对象,此时线程b若执行下面代码块会所住o2对象,然后两个线程都无法继续往下执行了(01,02都被锁住)

5.定时器-如何使线程间隔一定时间运行一次?

1. 使用框架里的定时器(如Spring)

2. 使用java.util.Timer(java自带的定时器)

2.1 Timer实例(可转为匿名内部类方式)
Timer t=new Timer();
t.schedule(new LogTimeTask(),new Date(),1000);
class LogTimeTask extends TimerTask{
    @Override
    public void run(){
        System.out.println("Now time: "+new Date());
    }
}

3. 使用Thread.sleep(不常用)

6.线程的常用方法

setName()               //设置名称(默认名称:Thread-n++)
getName()               //获取名称
currentThread()         //返回当前执行线程
sleep()                 //线程睡眠/阻塞,参数为毫秒(static方法,不受实例影响)
join()                  //合并线程(阻塞当前,加入其它线程)
interrupt()             //终止睡眠(通过异常处理机制实现)
yield()                 //暂停/让步线程
setPriority(int)        //设置线程优先级(1-10,默认5)
getPriority()           //获取线程优先级
setDameon(true)         //转守护线程

7.关于线程的方法

//Object方法
wait()                  //使关于此对象的活动线程永久睡眠
notify()                //使关于此对象的睡眠线程苏醒

8.线程的少用方法

stop()                  //强制终止线程(过时,容易丢包)

尾言

此文章写于本人学习多线程的过程中,非权威参考。
如有疑难或谬误,请指出。
如君阅后有所进益,不胜荣幸。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 游动-白 设计师:上身试试 返回首页