Java并发(进程、线程、多线程,使用)
进程和线程
-
定义
进程:进程是计算机正在运行的一个独立的应用程序。
线程:线程是组成进程的基本单位,可以完成特定的功能,一个进程是由一个或多个线程组成的。
应用程序是静态的,进程和线程是动态的,有创建有销毁。
-
两者的区别:
-
进程在运行时有独立的内存空间,各进程之间互不干扰。
-
线程是共享内存空间的,但是每个线程的执行都是相互独立的,单独的线程是无法执行的,由进程来控制多个线程的执行。
-
多线程
-
定义
多线程是指在一个进程中,多个线程同时执行,这里说的同时执行并不是真正意义的同时执行。
系统会为每个线程分配CPU资源,在某个具体的时间段内CPU资源会被一个线程占用,在不同的时间段内由不同的线程来占用CPU资源,所以多个线程还是在交替执行,只不过因为CPU运行速度太快,我们感觉是在同时执行。
-
优点
- 系统资源得到更合理的利用。
- 程序设计更加简洁。
- 程序响应更快,运行效率更高。
-
缺点
-
需要更多的内存空间来支持。
-
多线程并发访问的情况可能会影响数据的准确性。
-
数据被多线程共享,可能会出现死锁的情况
多线程并发--》数据不准确-->线程同步解决--》死锁
-
Java中使用线程
-
继承Thread类
- 创建自定义类并继承Thread类。
- 重写Thread类中的run方法,并编写程序的业务逻辑代码。
public class MyThread extends Thread { @Override public void run() { //业务代码 } }
- 使用
public static void main(String[] args) { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); //不能用以下这种方法,否则就不是启动线程了,只是顺序执行两个线程的方法,即普通方法的调用 myThread1.run(); myThread2.run(); //这才是正确的启动线程的方法 myThread1.start(); myThread2.start(); }
不能通过run方法来调用线程的任务,因为run方法调用相当于普通对象的执行,并不会去抢占CPU资 源。
只有通过start方法才能开启线程,进而去抢占CPU资源,当某个线程抢占到CPU资源后,会自动调用run方法。
-
实现Runnable接口
-
创建自定义类并实现Runnable接口。
-
实现run方法,编写该线程的业务逻辑代码。
public class MyRunnable implements Runnable { @Override public void run() { //业务逻辑代码 } }
-
使用
public static void main(String[] args) { MyRunnable myRunnable1 = new MyRunnable(); MyRunnable myRunnable2 = new MyRunnable(); //不能用以下这种方法,否则就不是启动线程了,只是顺序执行两个线程的方法,即普通方法的调用 myRunnable1.run(); myRunnable2.run(); //也不能用以下这种方法,因为Runnable并没有start方法 myRunnable1.start(); myRunnable2.start(); //正确的方法 Thread thread = new Thread(); thread.start(); }
Runable不能使用start的原因是
首先要搞清楚线程和任务的关系,线程是去抢占CPU资源的,任务是具体执行业务逻辑的,线程内部会包含一个任务,线程启动(start),抢占到CPU资源后,任务就开始执行(run)。
所以我们Runnable里的run只是写了业务逻辑代码(任务),它是单独分出来了,如果此时不分配给线程他就是个死的东西,而Thread类集成了线程和任务,所以继承Thread类后可以直接start启动。
-
-
两种方式的区别
1、MyThread,继承Thread类的方式,直接在类中重写run方法,使用的时候,直接实例化MyThread,start即可,因为Thread内部存在Runnable。
2、MyRunnbale,实现 Runnable 接口的方法,在实现类中重写run方法,使用的时候,需要先创建Thread对象,并将MyRunnable注入到Thread中,再Thread.start。
实际开发中推荐使用第二种方式。