多线程的基本知识

接下来就不多说废话了,今天对多线程的知识来聊聊:

一、并发和并行:用下面这张图就可以解释了

 

 

 

 二、进程和线程(这里就简单介绍一点,我主要是用代码讲解):

进程:一个内存中运行的应用程序

 线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程

三、线程也是一种对象,线程的创建有两种方式:Runnable接口或者继承了Thread类的对象才能成为线程

继承Thread类:

1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把 run()方法称为线程执行体。

2. 创建Thread子类的实例,即创建了线程对象

3. 调用线程对象的start()方法来启动该线程,执行run方法

代码实列:

 1 public class MyThread extends Thread{
 2     //定义指定线程的构造方法
 3     public MyThread(String name) {
 4         //super关键字调用父类参数的构造方法
 5         super(name);
 6     }
 7     //重写run方法,完成此线程执行的任务
 8     @Override
 9     public void run() {
10         for (int i=0;i<6;i++){
11             System.out.println(getName()+"正在执行"+i);
12         }
13     }
14 }

测试类:

 1 public class Demo1 {
 2     public static void main(String[] args) {
 3         //有参构造函数初始化
 4         MyThread myThread =new MyThread("新线程!");
 5         myThread.start();//启动线程
 6         //在main方法中执行循环
 7         for (int i=0;i<6;i++){
 8             System.out.println("子线程执行"+i);
 9         }
10     }
11 }

运行结果:

 

 

 

实现Runnable接口

1. 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正 的线程对象。

3. 调用线程对象的start()方法来启动线程。

代码实现:

public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i=0;i<6;i++){
Thread thread = Thread.currentThread();
String name = thread.getName();
System.out.println(name+"\t"+i);
}
}
}

测试代码:

 1 public class Demo1 {
 2     public static void main(String[] args) {
 3         //创建自定义类对象 线程任务对象
 4         MyRunnable runnable = new MyRunnable();
 5         Thread thread = new Thread(runnable,"张三");
 6         thread.start();//启动线程
 7         for (int i=0;i<6;i++){
 8             System.out.println("李四"+i);
 9         }
10     }
11 }

运行结果:

 

 

实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象来控制线程的,熟悉Thread类的常用方法是进行多线程编程的基础。

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

总结:实现Runnable接口比继承Thread类所具有的优势:

1. 适合多个相同的程序代码的线程去共享同一个资源。

2. 可以避免java中的单继承的局限性。

3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。

4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

 四、线程安全

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

事例分析:电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “喜羊羊与灰太狼”,本次电影的座位共100个。 我们来模拟电影院的售票窗口,实现多个窗口同时卖 “喜羊羊与灰太狼”这场电影票(多个窗口一起卖这100张票) 需要窗口,采用线程对象来模拟;需要票,Runnable接口子类来模拟。

代码实现:

 1 public class Ticket implements Runnable {
 2     private int ticket =10;
 3     //创建一个锁对象
 4     Object obj = new Object();
 5     @Override
 6     public void run() {
 7         //每个窗口卖票的操作
 8         //窗口 永远开启
 9         while (true){
10             synchronized (obj) {
11                 if (ticket > 0) {//有票 可以卖
12                     //出票操作
13                     //使用sleep模拟一下出票时间
14                     try {
15                         Thread.sleep(1000);
16                     } catch (InterruptedException e) {
17                         e.printStackTrace();
18                     }
19                     //获取当前线程对象的名字
20                     System.out.println(Thread.currentThread().getName() + "正在卖" + ticket--);
21                 }
22             }
23         }
24     }
25 }

测试类:

 1 public class TicketTest {
 2     public static void main(String[] args) {
 3         //创建线程任务对象
 4         Ticket ticket = new Ticket();
 5         //创建三个窗口对象
 6         Thread thread1 = new Thread(ticket,"窗口1");
 7         Thread thread2 = new Thread(ticket,"窗口2");
 8         Thread thread3 = new Thread(ticket,"窗口3");
 9         //三个窗口同时卖票
10         thread1.start();
11         thread2.start();
12         thread3.start();
13     }
14 }

输出结果:

 

posted @ 2021-09-17 11:50  金涛骇浪  阅读(52)  评论(0编辑  收藏  举报