栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java学习之【多线程】

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java学习之【多线程】

进程和线程 什么是进程

1、进程是一个应用程序

什么是线程

1、线程是一个进程中的执行场景/执行单元

2、一个进程可以启动多个线程

进程和线程的关系

1、进程A和进程B的内存不共享

2、线程A和线程B【Java中】

(1)堆内存和方法区内存共享

(2)栈内存独立,一个线程一个栈

多线程

使用了多线程机制,main方法结束了,只是主线程结束,主栈空了,其他的栈(线程)可能还在压栈弹栈。

目的

1、提高程序的处理效率

实现

1、第一种方式

// 编写一个类,直接继承java.lang.Thread,重写run方法

public class Test {
    public static void main(String[] args) {
        // 这里是main方法,这里的代码属于主线程,在主栈中运行
        // 新建一个分支线程
        MyThread myThread = new MyThread();
        // 启动线程
        myThread.start();
        // 以下代码还是运行在主线程中
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程---->" + i);
        }
    }
}

class MyThread extends Thread{
    public void run(){
        for (int i = 0; i < 3; i++) {
            System.out.println("分支线程----->" + i);
        }
    }
}

(1)start()方法作用:启动一个分支线程, 在JVM中开辟一个新的栈空间,这段代码任务完成之后,就瞬间结束了。只是为了开启一个新的栈空间,只要开辟成功,线程就启动成功。

(2)启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。

(3)run方法在分支栈的栈底部,main方法在主栈的底部。run和main是平级的。

(4)若是直接调用run()方法,则不会启动线程,不会分配新的分支栈。

2、第二种方式

// 实现线程的第二种方式,编写一个类实现java.lang.Runnable接口

public class Test {
    public static void main(String[] args) {
        // 创建一个可运行的对象
        MyRunnable r = new MyRunnable();
        // 将可运行的对象封装成一个线程对象
        Thread t = new Thread(r);
        // 可合并成一行:Thread t = new Thread(new MyRunnable());
        // 启动线程
        t.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程---->" + i);
        }
    }
}

class MyRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("分支线程---->" + i);
        }
    }
线程生命周期 

1、新建状态

2、就绪状态

3、运行状态

4、阻塞状态

5、死亡状态

6、图解:

线程常用方法 设置线程名字

myThread.setName("thread");

获取线程名字

// 默认线程名字:Thread-数字【数字从0开始】

myThread.getName();

获得当前线程的引用 

作用:获得当前线程的引用,当t1线程执行时,获取的就是t1线程的引用;当t2线程执行时,获取的就是t2线程的引用

Thread currentThread = Thread.currentThread();

sleep()

静态方法,参数是毫秒

作用:让当前线程进入“阻塞”状态,放弃占有CPU时间片,让给其他线程使用【间隔特定的时间,实现】

interrupt()【中断线程】

实例方法

作用:中断线程的睡眠【依靠java中的异常处理机制】

t.interrupt();

stop()【强行终止线程,现在已过时】 

作用:强行终止线程,缺点是容易丢失数据,不建议使用

代替实现:设置run标志,满足条件则线程运行,否则return

t.stop(); 

线程调度  常见的线程调度模型 抢占式调度模型

哪个线程的优先级比较高,抢到的CPU时间片的概率就高一些,Java采用的就是抢占式调度模型。

均分式调度模型

平均分配CPU时间片,每个线程占有的CPU时间片时间长度一样。

常用的线程调度方法 优先级分类

1、最低优先级为1,最高优先级为10,默认为5

设置线程的优先级

void setPriority(int newPriority)

获取线程的优先级

int getPriority()

让位方法

1、static void yield()

2、作用:暂停当前正在执行的线程对象,并执行其他线程。

3、yield()不是阻塞方法,让当前线程让位,让给其他线程使用,执行结果会让当前线程从“运行状态”回到“就绪状态”。

合并线程

1、void join()

2、作用:当前线程进入阻塞,调用这个方法的线程执行,知道这个线程结束,原线程才继续执行。

线程安全 多线程并发情况下数据不安全的情况

1、多线程并发

2、有共享数据

3、共享数据有修改的行为

如何解决线程安全问题

1、线程排队执行,不能并发,用排队执行解决线程安全问题,这种机制叫做:线程同步机制

2、缺点:会牺牲一部分效率,但是安全性问题是第一位

线程同步 异步编程模型

线程t1和线程t2,各自执行,t1和t2互不影响,谁也不需要等待谁,其实就是多线程并发

同步编程模型

线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行结束,或者说在t2线程执行的时候,必须等待t1线程结束,其实就是排队。

语法

第一种方法:

synchronized(共享对象){

        // 线程同步代码块

}

注意:1、synchronized后面小括号中传的这个“数据”是相当关键的,这个数据必须是多线程共享的数据。才能达到多线程排队。

2、()中写的内容

(1)假设有t1、t2、t3、t4、t5,有5个线程,只希望t1,t2,t3排队,t4,t5不排队,一定要在()内写一个t1,t2,t3共享的对象,而这个对象对于t4,t5来说不是共享的

第二种方法:

public synchronized void m1(){}

注意:synchronized出现在实例方法上,一定锁的是this,所以这种方式不灵活

缺点:表示整个方法体都需要同步,可能会无故扩大同步的范围,导致程序的执行效率降低,所以这种方式不常用

优点:代码节俭,如果共享的对象就是this,并且需要同步的代码块是整个方法体,建议使用这种方式

第三种方法:在静态方法上使用synchronized,表示找类锁,类锁永远只有1把,就算创建了100个对象,也只有一个类锁。 

哪些变量会有安全问题

1、局部变量:栈,局部变量不可能在多线程共享。

2、静态变量:方法区,可能存在

3、实例变量:堆,可能存在

局部变量情况

建议使用:StringBuilder,不存在线程安全问题,效率高

死锁

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/781860.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号