Java-多线程

本文详细介绍了Java中的多线程概念,包括程序、进程和线程的区别,单核与多核CPU对并发的影响,以及多线程的优势。讲解了两种线程创建方式,线程的常用方法,线程生命周期,线程控制和同步机制。深入探讨了Synchronized锁和Lock锁的使用,对比了两者的特性。内容涵盖了线程安全问题及其解决方案,旨在帮助开发者更好地理解和应用Java多线程技术。
摘要由CSDN通过智能技术生成

Java-多线程

1 程序、进程、线程

名称描述
程序为完成指定任务,用某种语言编写的一组指令的集合
进程程序的一次执行过程,是操作系统分配资源的最小单位
线程进程的进一步细化,是程序内部的一条执行路径

2 单核CPU和多核CPU

2.1 CPU

单核CPU其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程的任务

多核CPU才能更好的发挥多线程的效率

一个Java应用程序Java.exe其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程.如果发生异常,会影响主线程

2.2 并行和并发

名称描述
并行多个CPU同时执行多个任务,比如多个人同时做不同的事
并发一个CPU同时执行多个任务,比如秒杀,多个人做同一件事

2.3 多线程的优点

1 提高应用程序的响应,对图形化界面更有意义,可增强用户体验

2 提高计算机系统CPU的利用率

3 改善程序结构.将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

2.4 应用场景

1 程序需要同时执行两个或多个任务

2 程序需要实现一些需要等待的任务时,如用户输入,文件读写操作,网络操作,搜索等

3 需要一些后台运行的程序时

3 线程的创建

创建线程有两种方式,但是启动线程只能通过调用Thread类对象中的start方法

3.1 方法一

/*
创建线程的第一种方式
	创建类 继承 Thread
	复写run()方法,run()方法就等于新线程中的main方法,是程序的起点和终点
	启动线程:手动调用当前线程的start()方法,而不是run()方法
*/
public class Test1{
public static void test(){
    //创建线程类对象
    Thread t =new Test();
    //启动线程
    t.start();
    for(int i =0;i<10;i++){
        System.out.println("main->"+i);
    }
  }
}
class Test extends Thread{
    @Override
    public void run(){
        for(int i = 0;i<10;i++){
          System.out.println("run->"+i);  
        } 
    }
}

3.2 方法二

/*
创建线程的第二种方式
	实现Runnable接口,覆写run()方法
	启动线程,调用Thread类对象中的start方法
*/
public class Test1{
public static void test(){
    //创建线程类对象
    Thread t =new Test();
    //启动线程
    t.start();
    for(int i =0;i<10;i++){
        System.out.println("main->"+i);
    }
  }
}
class Test implements Runnable{
    @Override
    public void run(){
        for(int i = 0;i<10;i++){
          System.out.println("run->"+i);  
        } 
    }
}

4 线程的常用方法

Start() : 启动线程

setName() : 设置线程的名字,默认是Thread-0 , Thread-1 以此类推

getName() : 获取线程的名字

setPriority() : 设置优先级 , 1-10 , 10个级别,1最小,10最高

getPriority() : 获取优先级

Static currentThread() : 获取当前线程的内存地址(线程对象)

Static sleep() : 让当前线程进入睡眠状态,参数是毫秒数

5 线程的生命周期

一个线程完整的生命周期通常要经历五个阶段

阶段描述
新建新生的线程属于新建状态
就绪新生线程被start()后,没分配到CPU资源时
运行分配到CPU资源开始运行
阻塞被认为挂起或IO操作时
死亡线程完成全部工作或出现异常和强制终止时

image-20210701190423234

6 线程控制

方法功能
isAlive()判断线程是否存活
getPriority()获得线程的优先级数值
setPriority()设置线程的优先级数值
Thread.sleep()让当前线程睡眠指定毫秒数
join()调用某线程的该方法,并与当前线程合并
yield()让出CPU,当前线程进入就绪队列
wait()当前线程进入wait pool
notify()唤醒对象的wait pool的一个等待线程
notifyAll()唤醒对象的wait pool的所有等待线程

7 线程同步

多个线程执行的不确定性引起执行结果的不稳定

多个线程对账本的共享,会造成操作的不完整性,会破坏数据

多线程出现了安全问题

问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有 执行完,另一个线程参与进来执行。导致共享数据的错误。

解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以 参与执行。

8 Synchronized锁

对于并发工作,你需要某种方式来防 止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法 就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须 锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁 之时,另一个任务就可以锁定并使用它了。

同步方法

Synchronized的使用方法

Java对于多线程的安全问题提供了专业的解决方式:同步机制

同步代码块:

synchronized (对象){

// 需要被同步的代码;

}

synchronized还可以放在方法声明中,表示整个方法为同步方法。

例如:

public synchronized void show (String name){

….

}

注意:

必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就

无法保证共享资源的安全

一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方 法共用同一把锁(this),同步代码块(指定需谨慎)

释放锁的操作

当前线程的同步方法、同步代码块执行结束。

当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、 该方法的继续执行。

当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导

致异常结束。

当前线程在同步代码块、同步方法中执行了线程对象的**wait()**方法,当前线 程暂停,并释放锁。

不会释放锁的操作

线程执行同步代码块或同步方法时,程序调用Thread.sleep()、

Thread.yield()方法暂停当前线程的执行

线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程

挂起,该线程不会释放锁(同步监视器)。

应尽量避免使用suspend()和resume()来控制线程

9 Lock锁

Lock()
class A{
private final ReentrantLock lock = new ReenTrantLock();  public void m(){
lock.lock();
try{
//保证线程安全的代码;
}
finally{
lock.unlock();
}
}
}
注意:如果同步代码有异常,要将unlock()写入finally语句块

synchronized 与 Lock 的对比

Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是 隐式锁,出了作用域自动释放

synchronized是JVM层面的,属于重量级,Lock是代码层面的,比较轻量级

Lock只有代码块锁,synchronized有代码块锁和方法锁

使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值