第八章:多线程

发布于:2023-01-21 ⋅ 阅读:(167) ⋅ 点赞:(0)

第八章:多线程

一,线程和进程

  • 进程 : 应用程序的执行实例,有独立的内存空间和系统资源,一个应用程序只有一个进程。
  • 线程: CPU调度和分派的基本单位,进程中执行运算的最小单位,可完成一个独立的顺序控制流程,一个进程中可以有多个线程。

我们window系统可以按下Ctrl+Alt+.选择任务管理器的进程选卡,来看当前执行的进程和线程。

二,多线程

什么是多线程

  • 如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”,多个线程交替占用CPU资源,而非真正的并行执行.

多线程优点

  • 充分利用CPU的资源
  • 简化编程模型
  • 带来良好的用户体验

三,实现多线程的三种方式

​ 主线程:主线程也就是我们一直都在用的main方法,我们要在主线程中通过java给的Thread类,去创建多线程,并启动关闭。(Thread类:Java提供了java.lang.Thread类支持多线程编程)

1.实现多线程的第一种方式:继承Thread类

​ 1.继承Thread类。

​ 2.重写它的run()方法。

​ 3.在主线程中创建该类对象。

​ 4.调用start()方法。

自定义线程类:
public class MyThread extends Thread {
	@Override
    public  void run() {
        for(int i=0;i<20;i++){
            System.out.println((i+1)+".\t你好,来自线程:"+Thread.currentThread().getName());
        }
    }
}
主线程:
public class Client{
    public static  void main(String[] args) {
        Thread t1 = new MyThread();
        Thread t2 = new MyThread();
        t1.start();
        t2.start();
    }
}
运行部分代码:

在这里插入图片描述

​ 主线程执行到start方法的时候,创建的对应线程就会启动,要注意,多个线程执行是一个抢CPU的过程,谁抢到CPU,谁就执行run方法,有可能执行一行代码或者多行。

2.实现多线程的第二种方式:实现Runnable接口

​ 1.实现Runnable接口。

​ 2.重写它的run()方法。

​ 3.在主线程中创建该类对象。

​ 4.把对象传入Thread的构造方法内。

​ 5.调用start()方法。

线程类:
public class MyThreadRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println((i + 1) + ".\t你好 来自线程:" + Thread.currentThread().getName());
        }
    }
}
主线程类:
public class Client {
    public static void main(String[] args) {
        MyThreadRunnable my = new MyThreadRunnable();//实例化线程类。
        Thread t1 = new Thread(my);//把接口对象传入Thread构造方法中。
        Thread t2 = new Thread(my);
        t1.start();
        t2.start();
    }
}
部分运行结果:

在这里插入图片描述

1.实现多线程的第一种方式:实现Callable接口

​ 1.实现Callable接口。

​ 2.重写它的call()方法。

​ 3.在主线程中创建该类对象。

​ 4.把对象传入FutureTask的构造方法内。

​ 5.再把FutureTask对象传入Thread构造方法内。

​ 6.调用start()方法。

线程类:
public class CallableThread implements Callable<String> {
    @Override
    public String call() throws Exception {
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
        return "success";
    }
}
主线程类:
public class Client {
    public static void main(String[] args) {
        CallableThread ct = new CallableThread();
        FutureTask<String> ft1 = new FutureTask<>(ct);
        FutureTask<String> ft2 = new FutureTask<>(ct);
        Thread t1 = new Thread(ft1);
        Thread t2 = new Thread(ft2);
        t1.start();
        t2.start();
    }
}

注意:新建一个线程要新建一个FutureTask和Thread。

部分运行结果:

在这里插入图片描述

四,线程的状态

  • 创建状态: 在实例化一个Thread的时候,是该状态。
  • 就绪状态: 在执行start方法的时候,是就绪状态。
  • 运行状态: 在该线程抢到CPU的时候,是运行状态。
  • 阻塞状态: 等待用户输入,线程休眠等等进入阻塞状态。
  • 死亡状态:线程自然执行完毕或者外部干涉终止线程时,进入死亡状态。

在这里插入图片描述

五,线程调度

​ 线程调度:线程调度是指按照特定机制为多个线程分配CPU的使用权。

​ 我们接下来了解一下调度方法。

在这里插入图片描述

线程优先级

  • 线程优先级由1~10表示,1最低,默认优先级为5
  • 优先级高的线程获得CPU资源的概率较大

注意:不是优先级越大,就一定能抢到比优先级比自己小的线程。

public static void main(String[] args) {
		Thread t1 = new Thread(new MyThread(),"线程A");//第二个参数设置线程名
        Thread t2 = new Thread(new MyThread(),"线程B");
		t1.setPriority(Thread.MAX_PRIORITY);
		t2.setPriority(Thread.MIN_PRIORITY);
       //省略代码……
	}}

线程休眠

  • 让线程暂时睡眠指定时长,线程进入阻塞状态
  • 睡眠时间过后线程会再进入可运行状态

​ 语法: 线程对象名.sleep(long mills);

​ 注意:millis为休眠时长,以毫秒为单位调用sleep()方法需处理InterruptedException异常

​ 示例:

  for (int i = 0; i < 10; i++) {
             System.out.println(i + 1 + "秒");
             try {
		Thread.sleep(1000); //休眠一秒
             } catch (InterruptedException e) {
	e.printStackTrace();
             }
  }

线程的强制运行

​ 谁调用了join()方法就强制执行谁,执行完该线程后,会正常执行其他线程。

  • join():强制执行当前线程,join写在哪个线程,就阻塞谁。

  • public final void join()

  • public final void join(long mills)

  • public final void join(long mills,int nanos)

    //millis:以毫秒为单位的等待时长
    nanos:要等待的附加纳秒时长
    需处理InterruptedException异常

线程的礼让

public static void yield(): 线程的礼让,只是提供一种可能,但是不能保证一定会实现礼让.

  • 暂停当前线程,允许其他具有相同优先级的线程获得运行机会
  • 该线程处于就绪状态,不转为阻塞状态
public void run(){
          for(int i=0;i<5;i++){
    	System.out.println(Thread.currentThread().
              getName()+"正在运行:"+i);
    	 if(i==3){
    	    System.out.print("线程礼让:");
	    Thread.yield();	
                    }
          } 
}

在这里插入图片描述

六,多线程共享数据引发的问题

多个线程操作同一共享资源时,将引发数据不安全问题,我们要解决这种问题,就要学习同步方法。

同步方法:synchronized

​ 使用synchronized修饰的方法控制对类成员变量的访问。

语法:

  • 访问修饰符 synchronized 返回类型 方法名(参数列表){……}
  • synchronized 访问修饰符 返回类型 方法名(参数列表){……}

// synchronized就是为当前的线程声明一把锁

同步代码块

​ 使用synchronized关键字修饰的代码块

语法:

synchronized(synObject){
//需要同步的代码
}

synObject为需同步的对象,通常为this,效果与同步方法相同。

补充:

多个并发线程访问同一资源的同步代码块时:

  • 同一时刻只能有一个线程进入synchronized(this)同步代码块

  • 当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定

  • 当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非

    synchronized(this)同步代码

七,线程安全的类型

  • StringBuffer
  • Vector
  • Hashtable

多个并发线程访问同一资源的同步代码块时:

  • 同一时刻只能有一个线程进入synchronized(this)同步代码块

  • 当一个线程访问一个synchronized(this)同步代码块时,其他synchronized(this)同步代码块同样被锁定

  • 当一个线程访问一个synchronized(this)同步代码块时,其他线程可以访问该资源的非

    synchronized(this)同步代码

七,线程安全的类型

  • StringBuffer
  • Vector
  • Hashtable

注意:线程安全的类型,效率低;没有线程安全的类型,效率高。为达到安全性和效率的平衡,可以根据实际场景来选择合适的类型

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到