Java多线程基础知识

1、进程和线程的区别

1.1、什么是进程

它是内存中一段独立的空间,负责当前程序的运行,当前这个线程负责当前程序中的所有运行细节。

比如,你启动qq程序,其实是电脑把qq程序加载到内存中,在内存中给qq程序分配一段独立运行空间,这片空间就负责qq的运行。你再启动word,也是同理。不同的应用程序运行过程中都需要在内存中分配自己独立的运行空间,彼此之间不影响。我们把每个应用程序在内存的独立空间称为当前应用程序的运行一个进程。

1.2、什么是线程

它位于线程中,负责当前进程中某个独立运行资格的空间。

比如,你启动qq程序可以同时和多个人聊天,把qq这个进程分成多个运行区域,每个独立的小区域就是一个线程。

1.3、进程和线程的关系

进程负责整个程序的运行,进程负责程序中某个具体功能的运行。一个进程至少应该有一个线程。

2、多线程运行的原理

在一个进程中,我们同时开启多个线程,让多个线程去完成某些功能。提高程序的运行效率。

本质上电脑上的程序是不可能同时运行的,原理就是cpu在线程中做时间片的切换,cpu在某个时刻点上,它其实只能运行一个程序,因为cpu处理的速度太快,cpu在线程之间做切换,就像同时运行多线程一样。

3、实现Java多线程的两种方式

3.1、extends方式

import java.util.Random;

public class ThreadWithExtends extends Thread {

    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        
        Random random = new Random();
        for(int i = 0; i < 10; i++) {
            try {
                //随机睡一会儿
                sleep(random.nextInt(10) * 100);
                System.out.println(threadName + "线程的run方法被调用了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        ThreadWithExtends thread1 = new ThreadWithExtends();
        ThreadWithExtends thread2 = new ThreadWithExtends();
        thread1.start();
        thread2.start();
    }
    
}  

 运行结果:

Thread-1线程的run方法被调用了
Thread-0线程的run方法被调用了
Thread-0线程的run方法被调用了
Thread-0线程的run方法被调用了
Thread-1线程的run方法被调用了
Thread-0线程的run方法被调用了
Thread-0线程的run方法被调用了
Thread-1线程的run方法被调用了
Thread-1线程的run方法被调用了
Thread-0线程的run方法被调用了
Thread-0线程的run方法被调用了
Thread-0线程的run方法被调用了
Thread-1线程的run方法被调用了
Thread-1线程的run方法被调用了
Thread-0线程的run方法被调用了
Thread-1线程的run方法被调用了
Thread-0线程的run方法被调用了
Thread-1线程的run方法被调用了
Thread-1线程的run方法被调用了
Thread-1线程的run方法被调用了

每次运行的结果都是不一样的,cpu在多个线程间做时间片的切换。

3.2、implements方式

import java.util.Random;

public class ThreadImplementsRunnable implements Runnable {
    
    @Override
    public void run() {
        String threadName = Thread.currentThread().getName();
        Random random = new Random();
        for(int i = 0; i < 10; i++) {
            try {
                Thread.sleep(random.nextInt(10) * 100);
                System.out.println(threadName + "的run方法被调用了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        Thread thread1 = new Thread(new ThreadImplementsRunnable(), "A线程");
        Thread thread2 = new Thread(new ThreadImplementsRunnable(), "B线程");
        thread1.start();
        thread2.start();
    }

}

运行结果:

B线程的run方法被调用了
B线程的run方法被调用了
B线程的run方法被调用了
A线程的run方法被调用了
B线程的run方法被调用了
B线程的run方法被调用了
B线程的run方法被调用了
A线程的run方法被调用了
B线程的run方法被调用了
A线程的run方法被调用了
B线程的run方法被调用了
B线程的run方法被调用了
A线程的run方法被调用了
A线程的run方法被调用了
B线程的run方法被调用了
A线程的run方法被调用了
A线程的run方法被调用了
A线程的run方法被调用了
A线程的run方法被调用了
A线程的run方法被调用了

每次运行的结果都是不一样的,cpu在多个线程间做时间片的切换。

3.3、这两种方式如何选择

先来两段代码

1、用extends方式实现。

public class Ticket1Thread extends Thread {

    private int ticketNumber = 10;
    
    @Override
    public void run() {
        while(ticketNumber >= 0) {
            System.out.println("票被" + this.getName() + "买走了,还剩下" + ticketNumber-- + "张票");
        }
    }
    
    public static void main(String[] args) {
        Ticket1Thread ticketThread1 = new Ticket1Thread();
        Ticket1Thread ticketThread2 = new Ticket1Thread();
        ticketThread1.start();
        ticketThread2.start();
    }
}

运行结果:

票被Thread-1买走了,还剩下10张票
票被Thread-1买走了,还剩下9张票
票被Thread-0买走了,还剩下10张票
票被Thread-1买走了,还剩下8张票
票被Thread-1买走了,还剩下7张票
票被Thread-1买走了,还剩下6张票
票被Thread-1买走了,还剩下5张票
票被Thread-0买走了,还剩下9张票
票被Thread-1买走了,还剩下4张票
票被Thread-0买走了,还剩下8张票
票被Thread-0买走了,还剩下7张票
票被Thread-0买走了,还剩下6张票
票被Thread-0买走了,还剩下5张票
票被Thread-1买走了,还剩下3张票
票被Thread-0买走了,还剩下4张票
票被Thread-1买走了,还剩下2张票
票被Thread-0买走了,还剩下3张票
票被Thread-0买走了,还剩下2张票
票被Thread-0买走了,还剩下1张票
票被Thread-1买走了,还剩下1张票
票被Thread-1买走了,还剩下0张票
票被Thread-0买走了,还剩下0张票

2、用implements方式实现

public class Ticket2Thread implements Runnable {

    private int ticketNumber = 10;
    
    @Override
    public void run() {
        while(ticketNumber >= 0) {
            System.out.println("票被" + Thread.currentThread().getName() + "买走了,还剩下" + ticketNumber-- + "张票");
        }
    }
    
    public static void main(String[] args) {
        Ticket2Thread tickThread2 = new Ticket2Thread();
        new Thread(tickThread2).start();
        new Thread(tickThread2).start();
    }
    
}

运行结果:

票被Thread-0买走了,还剩下10张票
票被Thread-0买走了,还剩下8张票
票被Thread-0买走了,还剩下7张票
票被Thread-0买走了,还剩下6张票
票被Thread-0买走了,还剩下5张票
票被Thread-0买走了,还剩下4张票
票被Thread-0买走了,还剩下3张票
票被Thread-0买走了,还剩下2张票
票被Thread-0买走了,还剩下1张票
票被Thread-0买走了,还剩下0张票
票被Thread-1买走了,还剩下9张票

3、结论:

(1)extends方式的缺点就是单继承,不方便扩展。而实现Runnable接口没有这个缺点。

(2)implements方式可以创建多个线程来执行同一任务,而且这多个线程之间还将共享同一个资源。extends方式不可以。

(3)implements方式实现了线程、代码、数据的分离,很好地体现了面向对象程序设计的思想。

实际开发中推荐使用implements方式实现多线程。

4、参考文献

https://www.cnblogs.com/jdonson/p/6567708.html

 
原文地址:https://www.cnblogs.com/onezg/p/8965666.html