多线程与并发编程(概念)

什么是进程?

        执行中的程序叫做进程(Process) ,是一个动态的概念。其实进程就是一个在内存中独立运行的程序空间 。
        现代操作系统比如Mac OS X, Linux Windows 等,都是支持 “多任务 的操作系统,叫 多任务 呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用逛淘宝,一边在听音乐,一边在用微信聊天,这就是多任务,至少同时有3 个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。

什么是线程?

        线程(Thread )是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
        有些进程还不止同时干一件事,比如微信,它可以同时进行打字聊天,视频聊天,朋友圈等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务 ,我们把进程内的这些
子任务 称为线程( Thread )。

进程、线程的区别

        乔布斯想开工厂生产手机,费劲力气,制作一条生产线,这个生产线上有很多的器件以及材料。一条生产线就是一个进程。 只有生产线是不够的,所以找五个工人来进行生产,这个工人
能够利用这些材料最终一步步的将手机做出来, 这五个工人就 是五个线程。为了提高生产率,有两种办法:
  •          一条生产线上多招些工人,一起来做手机,这样效率是成倍増长,即单进程多线程方式
  •         多条生产线,每个生产线上多个工人,即多进程多线程

  1. 线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
  2. 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
  3. 3进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号),某进程内的线程在其它进程不可见;
  4. 4 调度和切换:线程上下文切换比进程上下文切换要快得多。

什么是并发

         并发是指在一段时间内同时做多个事情。当有多个线程在运行时,如果只有一个CPU,这种情况下计算机操作系统会采用并发技术实现并发运行,具体做法是采用“ 时间片轮询算法,在一个时间段的线程代码运行时,其它线程处于就绪状。这种方式我们称之为并发。(Concurrent)。

  1. 串行(serial):一个CPU上,按顺序完成多个任务
  2. 2 并行(parallelism):指的是任务数小于等于cpu核数,即任务真的是一起执行的
  3. 3 并发(concurrency):一个CPU采用时间片管理方式,交替的处理多个任务。一般是是任务数多余cpu核数,通过操作系统的各种任务调度算法,实现用多个任务一起执行(实际上总有一些任务不在执行,因为切换任务的速度相当快,看上去一起执行而已)

 线程的执行特点

什么是主线程以及子线程

 

主线程

        当Java 程序启动时,一个线程会立刻运行,该线程通常叫做程序的主线程(main thread ),即 main 方法对应的线程,它是程序开始时就执行的。 Java应用程序会有一个 main 方法,是作为某个类的方法出现的。当程序启动时,该方法就会第一个自动的得到执行,并成为程序的主线程。也就是说,main 方法是一个应用的入口,也代表了这个应用的主线程。JVM 在执行 main 方法时 ,main 方法会进入到栈内存 ,JVM会通过操作系统开辟一条main 方法通向 cpu 的执行路径 ,cpu 就可以通过这个路径来执行main 方法 , 而这个路径有一个名字 , main( 主)线程。

主线程的特点

        它是产生其他子线程的线程。
它不一定是最后完成执行的线程,子线程可能在它结束之后还在运行。

子线程

        在主线程中创建并启动的线程,一般称之为子线程。

线程的创建

通过继承 Thread 类实现多线程

继承 Thread 类实现多线程的步骤:
1
  1. Java中负责实现线程功能的类是java.lang.Thread 类。 此种方式的缺点:如果我们的类已经继承了一个类(如小程序必须继承自 Applet 类),则无法再继承 Thread 类。
  2. 可以通过创建 Thread的实例来创建新的线程。
  3. 每个线程都是通过某个特定的Thread对象所对应的方法run( )来完成其操作的,方法run( )称为线程体。
  4. 通过调用Thread类的start()方法来启动一个线程。

通过继承Thread类实现多线程

public class TestThread extends Thread {//自
定义类继承Thread类
    //run()方法里是线程体
 public void run() {
     for (int i = 0; i < 10; i++) {
    System.out.println(this.getName() + ":" +i);//getName()方法是返回线程名称
        }    
    }
    public static void main(String[] args) {
        TestThread thread1 = new TestThread(); //创建线程对象
            thread1.start();//启动线程
        TestThread thread2 = new TestThread();
            thread2.start();
    }
}

通过Runnable接口实现多线程

        

        在开发中,我们应用更多的是通过Runnable 接口实现多线程。这种方式克服了继承Thread 类的缺点,即在实现 Runnable 接口的同时还可以继承某个类。
        从源码角度看,Thread 类也是实现了 Runnable 接口。 Runnable 接口的源码如下:
public class TestThread2 implements Runnable{
    //自定义类实现Runnable接口;
    //run()方法里是线程体;
     public void run() {
         for (int i = 0; i < 10; i++) {

        System.out.println(Thread.currentThread().getName() + ":" + i);
     }
 }
 public static void main(String[] args) {
        //创建线程对象,把实现了Runnable接口的对象
        作为参数传入;
         Thread thread1 = new Thread(new TestThread2());
                 thread1.start();//启动线程;
         Thread thread2 = new Thread(new TestThread2());
                 thread2.start();
     }
 }

线程状态和生命周期

        一个线程对象在它的生命周期内,需要经历5 个状态。

新生状态(New)

        用new 关键字建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start 方法
进入就绪状态。
2

就绪状态(Runnable)

        处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“ 线程就绪队列 ,等待系统为其分配 CPU 。就绪状态并不是执行状态,当系统选定一个等待执行的Thread 对象后,它会进入执行状态。一旦获得CPU ,线程就进入运行状态并自
动调用自己的 run 方法。有 4 种原因会导致线程进入就绪状态:
1 新建线程:调用 start() 方法,进入就绪状态;
2 阻塞线程:阻塞解除,进入就绪状态;
3 运行线程:调用 yield() 方法,直接进入就绪状态;
4 运行线程: JVM CPU 资源从本线程切换到其他线程。
3

运行状态(Running)

        在运行状态的线程执行自己run 方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“ 导致阻塞的事件 而进入阻塞状态。

4 阻塞状态(Blocked)

        阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。
4 种原因会导致阻塞:
  1.  执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。
  2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。
  3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。
  4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法。

死亡状态(Terminated)

        死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个。一个是正常运行的线程完成了它run()方法内的全部工作; 另一个是线程被强制终止,如通过执行stop() destroy() 方法来终止一个线程(注:stop()/destroy() 方法已经被 JDK 废弃, 不推荐使用)。
当一个线程进入死亡状态以后,就不能再回到其它状态了。
线程的使用

终止线程的典型方法

        
public class StopThread implements Runnable{
    private boolean flag = true;
    @Override
    public void run() {
      
    System.out.println(Thread.currentThread().getName()+" 线程开始");
            int i= 0;
            while(flag){
              System.out.println(Thread.currentThread().getName()+" "+i++);
                try {
                    Thread.sleep(1000);
               } catch
                 (InterruptedException e) {
                    e.printStackTrace();
               }
           }
      
                System.out.println(Thread.currentThread().getName()+" 线程结束");
   }
    public void stop(){
        this.flag = false;
   }

    public static void main(String[]args)throws Exception {
        System.out.println("主线程开始");
        StopThread st = new StopThread();
        Thread t1 = new Thread(st);
        t1.start();
        System.in.read();
        st.stop();
        System.out.println("主线程结束");
   }
}

线程休眠

        sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。sleep方法的参数为休眠的毫秒数。        
public class SleepThread implements Runnable
{
    @Override
    public void run() {
      
        System.out.println(Thread.currentThread().getName()+" 线程开始");
          for(int i=0;i<20;i++){             
                System.out.println(Thread.currentThread().getName()+" "+i);
                try {
                    //线程休眠1秒
                    Thread.sleep(1000);
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
           }
      System.out.println(Thread.currentThread().getName()+" 线程结束");
      }
    public static void main(String[] args) {
        System.out.println("主线程开始");
        Thread t = new Thread(new    SleepThread());
        t.start();
        System.out.println("主线程结束");
   }
 }

线程让步

        使用yield 方法时要注意的几点:
  •         yield是一个静态的方法。
  •         调用yield后,yield告诉当前线程把运行机会交给具有相同优先级的线程。
  •         yield不能保证,当前线程迅速从运行状态切换到就绪状态。
  •         yield只能是将当前线程从运行状态转换到就绪状态,而不能是等待或者阻塞状态。

public class TestyieldThread implements Runnable {
    @Override
    public void run() {
        for(int i=0;i<30;i++){
            if("Thread-0".equals(Thread.currentThread().getName()))
        {
                if(i == 0){
                    Thread.yield();
               }
           }
          
                System.out.println(Thread.currentThread().getName()+" "+i);
       }
   }
    public static void main(String[] args) {
        Thread t1 = new Thread(new TestyieldThread());
        Thread t2 = new Thread(new TestyieldThread());
        t1.start();
        t2.start();
   }
}

线程联合        

        当前线程邀请调用方法的线程优先执行,在调用方法的线程执行结束之前,当前线程不能再次执行。线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。这样,线程A就必须等待线

        程B 执行完毕后,才能继续执行。

 join方法的使用

        join()方法就是指调用该方法的线程在执行完run()方法后,再执行

         join方法后面的代码,即将两个线程合并,用于实现同步控制。

 class A implements Runnable{
    private Thread b;
    public A(Thread b){
        this.b = b;
   }

    @Override
    public void run() {
        for(int i=0;i<10;i++){
          
        System.out.println(Thread.currentThread().getName()+" A "+i);
            if(i == 5){
                try {
                    this.b.join();
               } catch
                (InterruptedException e) {
                    e.printStackTrace();
               }
           }
            try {
                Thread.sleep(1000);

           } catch (InterruptedException e){
                e.printStackTrace();
           }
       }
   }
 }

     class B implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<20;i++){
          
        System.out.println(Thread.currentThread().getName()+" B "+i);
            try {
                Thread.sleep(1000);
           } catch (InterruptedException e){
               e.printStackTrace();
           }
       }
   }
 }

 public class TestJoinThread {
    public static void main(String[] args) {
        Thread t1 = new Thread(new B());
        Thread t = new Thread(new A(t1));

        t.start();
        t1.start();

        for(int i=0;i<10;i++){
          System.out.println(Thread.currentThread().getName()+" "+i);
            if(i ==2){
                try {
                    t.join();
               } catch(InterruptedException e) {
                    e.printStackTrace();
               }
           }
            try {
                Thread.sleep(1000);
          } catch (InterruptedException e){
                e.printStackTrace();
           }
       }
   }
}

线程联合案例

        需求:
        实现爸爸让儿子买烟。
/**
 * 儿子买烟线程
 */
 class SonThread implements Runnable{
    @Override
    public void run() {        System.out.println("儿子出门买烟");
       System.out.println("儿子买烟需要10分钟");
        for(int i=0;i<10;i++){
            System.out.println("第"+i+"分钟");
            try {
                Thread.sleep(1000);
           } catch (InterruptedException e){
                e.printStackTrace();
           }
       }
        System.out.println("儿子买烟回来了");
   }
 }

 /**
 * 爸爸抽烟线程
 */
 class FatherThread implements Runnable{

    @Override
    public void run() {
        System.out.println("爸爸想抽烟,发现烟抽完了");
        System.out.println("爸爸让儿子去买一包红塔山");
        Thread t = new Thread(new SonThread());
        t.start();
        System.out.println("等待儿子买烟回来");
        try {
            t.join();
       } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("爸爸出门找儿子");
            System.exit(1);
       }

        System.out.println("爸爸高兴的接过烟,并把零钱给了儿子");
   }
}
public class TestJoinDemo {
    public static void main(String[] args) {
        System.out.println("爸爸和儿子买烟的故事");
        Thread t = new Thread(new
FatherThread());
        t.start();
   }
}

Thread类中的其他常用方法

获取当前线程名称

方式一

        this.getName()获取线程名称,该方法适用于继承 Thread 实现多线程方式。
class GetName1 extends Thread{
    @Override
    public void run() {
        System.out.println(this.getName());
   }
}

方式二

        Thread.currentThread().getName()获取线程名称,该方法适用于 实现Runnable 接口实现多线程方式。
class GetName2 implements Runnable{
     @Override
    public void run() {
      System.out.println(Thread.currentThread().getName());
   }
 }

设置线程的名称

方式一

        通过构造方法设置线程名称。

方式二

        通过setName() 方法设置线程名称。
thread . setName ( "SetName2" );

判断线程是否存活

         

isAlive() 方法: 判断当前的线程是否处于活动状态。
        活动状态是指线程已经启动且尚未终止,线程处于正在运行或准备开始运行的状态,就认为线程是存活的。
class Alive implements  Runnable{
    @Override
    public void run() {
        for(int i=0;i<4;i++){
         System.out.println(Thread.currentThread().getName()+" "+i);
            try {
    Thread.sleep(500);
           } catch (InterruptedException e){
                e.printStackTrace();
           }
       }
   }
}
public class TestAliveThread {
    public static void main(String[] args) {
        Thread thread = new Thread(new Alive());
        thread.setName("Alive");
        thread.start();
      System.out.println(thread.getName()+""+thread.isAlive());
        try {
            Thread.sleep(4000);
       } catch (InterruptedException e) {
            e.printStackTrace();
       }
      System.out.println(thread.getName()+""+thread.isAlive());
   }
}

线程的优先级

什么是线程的优先级

        每一个线程都是有优先级的,我们可以为每个线程定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程的优先级用数字表示,范围从1到 10 ,一个线程的缺省优
先级是 5
        Java 的线程优先级调度会委托给操作系统去处理,所以与具体的操作系统优先级有关,如非特别需要,一般无需设置线程优先级。
注意
        线程的优先级,不是说哪个线程优先执行,如果设置某个线程的优先级高。那就是有可能被执行的概率高。并不是优先执行。

 线程优先级的使用

        使用下列方法获得或设置线程对象的优先级。

  • int getPriority();
  • void setPriority(int newPriority);
注意:
        优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。
  • 38
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多线程并发编程Java中一个重要的概念和技术。如果你对这方面的知识感兴趣,以下是一些推荐的书籍: 1. 《Java并发编程实战》:这本书被称为Java并发的圣经,它详细介绍了Java中的并发编程概念、问题和解决方案。\[3\] 2. 《Java多线程编程核心技术》:这本书介绍了Java多线程编程的核心技术,包括线程的创建、同步、通信等方面的内容。 3. 《Java 7并发编程实战手册》:这本书是一个实践指南,介绍了Java 7中的并发编程实践,包括使用Java并发工具和API等方面的内容。\[3\] 4. 《Java并发编程的艺术》:这本书介绍了在多核处理器的共享内存模型中的各种并发算法和技术。\[3\] 5. 《C++ Concurrency in Action》:这本书介绍了C++中的并发编程,包括线程的创建、同步、通信等方面的内容。\[3\] 这些书籍涵盖了多线程并发编程的核心概念、技术和实践,可以帮助你深入理解和应用这些知识。希望对你有帮助! #### 引用[.reference_title] - *1* [Java 并发和多线程](https://blog.csdn.net/m0_72674204/article/details/126332825)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [有什么好的并发编程书籍推荐?还真有一本](https://blog.csdn.net/epubit17/article/details/121733925)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [19本高并发编程书籍推荐](https://blog.csdn.net/liuhuiteng/article/details/106090113)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值