多线程基础

多线程基础,第1张

1.进程和线程基本概念 1.1 进程
  • 运行中的应用程序叫进程,每个进程运行时,进程负责了内存空间的划分。它是系统进行资源分配和调度的一个独立单位。

  • *** 作系统都是支持多进程的

  • Windows是多任务的 *** 作系统,那么Windows是同时运行多个应用程序吗?

1.2 线程 1.2.1线程概念和特点
  • 线程是轻重级的进程,是进程中一个负责程序执行的控制单元

  • 线程是由进程创建的(寄生在进程中)

  • 一个进程可以拥有多个线程,至少一个线程

  • 线程有几种状态(新建new,就绪Runnable,运行Running,阻塞Blocked,死亡Dead)

  • 开启多个线程是为了同时运行多部分代码,每个线程都 有自已的运行的内容,这个内容可以称线程要执行的任务。

  • 打开360卫士,就是打开一个进程,一个进程里面有很多代码,这些代码就是谁来执行的呢?线程来执行这些代码。

 

1.2.2 多线程
  • 多线程:在一个进程中有多个线程同时在执行不同的任务。

 

  • 多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务

  • 如:百度网盘、腾讯会议。

  • 同时执行多个任务的优势:减低CPU的闲置时间,从而提高CPU的利用率

1.2.3 计算机 *** 作系统的进化史
  • 当前 *** 作系统支持多线程

2.多线程的优缺点 2.1 多线程优点
  • 多线程最大的好处在于可以同时并发执行多个任务;

  • 多线程可以最大限度地减低CPU的闲置时间,从而提高CPU的利用率

2.2 多线程缺点
  • 线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;

  • 多线程需要协调和管理,所以需要CPU时间跟踪线程;

  • 线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;

  • 线程太多会导致控制太复杂,最终可能造成很多Bug

3.主线程
  • Java支持多线程

  • 主线程的特殊之处在于:

    • 任何一个Java程序启动时,一个线程立刻运行,它执行main方法,这个线程称为程序的主线程。

    • 一个Java应用程序至少有两个线程,一个是主线程负责main方法代码执行;一个是垃圾回收器线程,负责了回收垃圾。

4.创建线程 4.1 创建线程的三种方式
  • 方式一:继承Thread类

  • 方式二:实现Runnable接口

  • 方式三:实现Callable接口

4.2 继承Thread类
  • 继承Java.lang.Thread类,并重写run() 方法。

 

案例1:打印输出0-100的数字,创建两个线程交替执行  
package thread;

public class MyPrintThreadTest {
    public static void main(String[] args) throws InterruptedException {
        //创建2个线程对象
        MyPeintTread thread1=new MyPeintTread("线程1");
        MyPeintTread thread2=new MyPeintTread("线程2");
        //start()方法
//        让 *** 作系统启动一个线程执行MyPrintThread对象run方法
//       start方法调用之后,不会立即启动一个线程,线程的启动取决于 *** 作系统
        thread1.start();
        thread2.start();
        //打印main方法执行完毕
        System.out.println("main方法执行完毕");

    }
}

package thread;
//打印1-100数字
public class MyPeintTread extends Thread {
    //构造方法,传入线程名称
    public MyPeintTread(String name) throws InterruptedException {

        super(name);
    }
//    重写run方法,线程会调用run方法
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //this.getName() 获取线程的名称
            System.out.println(this.getName() + ":" + i);

        }
    }
}

 案例2:模拟龟兔赛跑
package thread;

class MyThread3 extends Thread{
    private int s=5;
    @Override
    public void run() {
        while(true){
//            try {
//                Thread.sleep(500);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            if (s<0){
                System.out.println("乌龟跑完了");
                break;
            }
            System.out.println("乌龟领先了,加油,还剩下"+s+"米");
            s--;
        }
    }
}
package thread;

public class MyThread4 extends Thread{
    private int s=5;

    @Override
    public void run() {
        while(true){
//            try {
//                Thread.sleep(500);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
            if (s<0){
                System.out.println("兔子跑完了");
                break;
            }
            System.out.println("兔子领先了,加油,还剩下"+s+"米");
            s--;
        }
    }
}
package thread;


public class RaceThreadTest {
    public static void main(String[] args) {
        MyThread3 myThread1=new MyThread3();
        MyThread4 myThread2=new MyThread4();
        myThread2.start();
        myThread1.start();
    }
}

案例1中的MyPrintThread要求继承ArrayList,如何创建多线程?继承Thread类就不能继承ArrayList,因为,java是单继承,所以就不合适了,也有的人认为让ArrayList继承Thread,MyPrintThread再继承ArrayList,或者Thread继承ArrayList,MyPrintThread再继承Thread,这两种方案都不可以,因为Thread和ArrayList是jdk提供的不能随便修改。

4.3 实现Runnable接口
  • 实现Java.lang.Runnable接口,并重写run() 方法;

注意:Runnable接口的存在主要是为了解决Java中不允许多继承的问题。

案例1:打印输出0-100的数字,创建两个线程交替执行
package runnable;

import java.util.ArrayList;

//实现Runnable线程类
public class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            try {
                //休眠500毫秒
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //获取执行run方法的当前线程的名称
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + ":" + i);
        }
    }
}
package runnable;

public class MyThreadTest {
    public static void main(String[] args) {
        //创建线程类
        MyThread t1 = new MyThread();
        //创建Thread对象包裹t1
        Thread thread1 = new Thread(t1);
        //启动线程
        thread1.start();

        //创建线程类
        MyThread t2 = new MyThread();
        //创建Thread对象包裹t1
        Thread thread2 = new Thread(t2);
        //启动线程
        thread2.start();
    }
}
 案例2:模拟龟兔赛跑
package runnable;

//乌龟数线程
public class MyThread3 implements Runnable {
    private int s = 5;

    @Override
    public void run() {
        while (true) {
            if (s < 0) {
                System.out.println("乌龟跑完了");
                break;
            }
            System.out.println("乌龟加油,还剩下" + s + "米");
            s--;
        }
    }
}
package runnable;

//兔子线程
public class MyThread4 implements Runnable {
    private int s = 5;

    @Override
    public void run() {
        while (true) {
            if (s < 0) {
                System.out.println("兔子跑完了");
                break;
            }
            System.out.println("兔子加油,还剩下" + s + "米");
            s--;
        }
    }
}
package runnable;

public class RaceMyThreadTest {
    public static void main(String[] args) {
        //运行main方法线程主线程

        //创建兔子线程类对象
        MyThread4 myThread1 = new MyThread4();
        //创建Thread对象
        Thread t1 = new Thread(myThread1);
        //启动线程
        t1.start();

        //创建乌龟线程类对象
        MyThread3 myThread2 = new MyThread3();
        //创建Thread对象
        Thread t2 = new Thread(myThread2);
        //启动线程
        t2.start();

        System.out.println("main方法完成");
    }
}

 案例1中MyThread如何实现返回值?

4.4 实现Callable接口
  • 使用Callable和Future创建线程

    使用Callable创建线程和Runnable接口方式创建线程比较相似,不同的是,Callable接口提供了一个call() 方法作为线程执行体,而Runnable接口提供的是run()方法,同时,call()方法可以有返回值,而且需要用FutureTask类来包装Callable对象。

  • 步骤:

    1、定义实现Callable接口的类,实现call() 方法

    2、创建Callable实现类实例,通过FutureTask类来包装Callable对象,该对象封装了Callable对象的call()方法的返回值。

    3、将创建的FutureTask对象作为target参数传入,创建Thread线程实例并启动新线程。

    4、调用FutureTask对象的get方法获取返回值。

案例  
package callable;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableThreadTest {
    public static void main(String[] args) throws ExecutionException,InterruptedException {
        //创建CallableThread对象
        CallableThread ct=new CallableThread();
        //创建FutureTask对象,获取返回值
        FutureTask task=new FutureTask<>(ct);
//        创建Tread对象
        Thread t=new Thread(task);
        t.start();
//        FutureTask对象的get()方法获取CallableThread对象的call方法的返回值
        //FutureTask的get方法会阻塞代码,等待t线程运行结束,获取返回值,才会往下运行
        Integer sum=task.get();
        System.out.println(sum);

    }
}
package callable;

import java.util.concurrent.Callable;

public class CallableThread implements Callable {


    @Override
    public Integer call() throws Exception {
       int sum=0;
        for (int i = 0; i <=100 ; i++) {
            sum+=i;
        }
        return sum;
    }
}

 案例2:
package user;

public class User {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package user;

import java.util.ArrayList;
import java.util.List;

public class UserService {
    public List selectList(){
        List users=new ArrayList<>();
        User u1=new User("jack",20);
        User u2=new User("jim",25);
        users.add(u1);
        users.add(u2);
        return users;
    }
}
package user;

import java.util.List;
import java.util.concurrent.Callable;

public class UserThread implements Callable> {
    private UserService userService;

    public UserThread(UserService userService) {
        this.userService = userService;
    }

    @Override
    public List call() throws Exception{
       List users=userService.selectList();
       return  users;
    }
}
package user;

import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class UserThreadTest {
    public static void main(String[] args) {
        //创建serService对象
        UserService userService = new UserService();
        //创建UserThread对象
        UserThread userThread=new UserThread(userService);
        //创建FutureFask对象
        FutureTask> task=new FutureTask<>(userThread);
        //创建Threa对象
        Thread t=new Thread(task);
        //启动线程
        t.start();
        //FutureTack对象get方法获取返回值
        try {
            List users=task.get();
            System.out.println(users);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}
4.5 创建线程的三种方式-比较
  • 继承Thread类:

    • 优势:Thread类已实现了Runnable接口,故使用更简单

    • 劣势:无法继承其它父类

  • 实现Runnable接口:

    • 优势:可以继承其它类

    • 劣势:编程方式稍微复杂,多写一行代码

  • 实现Callable接口:

    • 类似于Runnable,方法可以有返回值,并且可以抛出异常。但是Runnable不行。

5.线程的状态
  • Java中线程状态转换:

Java中线程存在以下几种状态 :

  • 新线程:

    • 新创建了一个线程对象,此时它仅仅作为一个对象实例存在, JVM没有为其分配CPU时间片和其他线程运行资源。

  • 就绪状态:

    • 在处于创建状态的线程中调用start方法将线程的状态转换为就绪状态。这时,线程已经得到除CPU时间之外的其它系统资源,只等JVM的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU时间片的机会

  • 运行状态:

    • 就绪态的线程获得cpu就进入运行态

  • 等待/阻塞:

    • 阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

  • 死亡状态:

    • 线程执行完它的任务时,由JVM收回线程占用的资源

 

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/724205.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存