进程与线程
进程
在操作系统中,每个独立的执行的程序都是一个进程,计算机上都是安装的多任务操作系统,能同时执行多个应用程序。
在多任务的操作系统中,逻辑上是同时运行的,实际上并不是,所有应用程序都是由CPU执行,对于CPU来说,一个时间点只能运行一个程序。操作系统会为每个进程分配一段有限的CPU占有时间,占有时间都在同一条时间线上。
线程
每个运行的程序都是一个进程,在一个程序中还可以有多个可执行的单元,每个单元称为线程。每个进程中至少存在一个线程。
当启动Java程序时,就会产生一个进程,并创建一个线程,执行主函数的代码。代码会按调用顺序从上往下依次执行,称为单线程程序,如果希望程序多端代码交替运行,则需要创建多个线程。
线程和进程一样都是由CPU执行的,在同一条时间线上,逻辑上是同时执行的。
线程创建
Java中提供了两种多线程的实现方式,一种是继承java.lang
包下的Thread
类,重写run()
方法,在run方法类实现线程代码;另一种方法是实现java.lang.Runable
接口,重写run()
方法。
继承Thread
//继承Thread
import java.lang.Thread;
public class App {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.run();
for (int i=0;i<10;i++){
System.out.println("main is running");
}
}
static class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println("MyThread is running");
}
}
}
}
上面代码演示了一个单线的案例,run是继承Thread创建的一个线程,该线程结束后主程序才结束。
多线程需要持续运行的程序才能体现出来,系统分配CPU占用时间。
//多线程演示用start()方法,run会抢占线程
import java.lang.Thread;
public class App {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
while (true){
System.out.println("main is running");
}
}
static class MyThread extends Thread{
@Override
public void run() {
while (true){
System.out.println("MyThread is running");
}
}
}
}
如图看出两个线程是交替运行,由系统膏分配CPU占用时间。注意:用
start()
开启线程,不能用run()
会抢占线程。
多线程与单线程的区别在于动态占用CPU时间。单线程独占,多线程系统分配。
实现Runnable接口
Thread创建线程的弊端在于必须继承Thread,而已经存在继承关系的类就不能在继承了(Java单继承原理)。Runnable接口的优势在于其只实现了Thread的run方法,只需重写逻辑,并将Runnable作为参数传递给Thread,这样就不用继承Thread。
//将Runnable作为参数传到Thread
public class App {
public static void main(String[] args) {
Thread thread=new Thread(new MyRunnable());
thread.start();
Thread thread1=new Thread(new MyRunnableOne());
thread1.start();
}
static class MyRunnable implements Runnable{
@Override
public void run() {
while (true){
System.out.println("Runnable is running");
}
}
}
static class MyRunnableOne implements Runnable{
@Override
public void run() {
while (true){
System.out.println("RunnableOne is running");
}
}
}
}
线程应用
Thread和Runnable都能实现线程,那么有什么区别呢?
Thread是独立的
class MyThreadOne extends Thread{
private int total=100;
@Override
public void run() {
while (total>=1){
//获取当前线程
Thread thread=Thread.currentThread();
//获取线程名
String th_name=thread.getName();
System.out.println(th_name+"正在发售第"+total--+"张票");
}
}
}
public class MainApp {
public static void main(String[] args) {
MyThreadOne myThreadOne1=new MyThreadOne();
MyThreadOne myThreadOne2=new MyThreadOne();
MyThreadOne myThreadOne3=new MyThreadOne();
myThreadOne1.start();
myThreadOne2.start();
myThreadOne3.start();
}
}
代码实现了继承Thread,内部有100张票,三个线程发售。
由结果可以发现线程Thread-0和1,2都各自发售了第100次票,显然他们是独立的。
Runnable线程是合作的
package com.company.runnable;
class MyRunnable implements Runnable{
private int total=100;
@Override
public void run() {
while (total>=1){
//获取当前线程
Thread thread=Thread.currentThread();
//获取线程名
String th_name=thread.getName();
System.out.println(th_name+"正在发售第"+total--+"张票");
}
}
}
public class MainApp {
public static void main(String[] args) {
Thread myThreadOne1=new Thread(new MyRunnable());
Thread myThreadOne2=new Thread(new MyRunnable());
Thread myThreadOne3=new Thread(new MyRunnable());
myThreadOne1.start();
myThreadOne2.start();
myThreadOne3.start();
}
}
除了上面案例外由多态特性
也可以理解,通过实例化Thread
创建是不同的对象,对象间是独立的,毫无关联的。而实现Runnable
接口,仅仅是重写了run()
方法,他们共享成员变量。
线程生命周期及状态转换
Java对象中任何对象都有生命周期,线程也不例外,Thread对象创建完成,线程的生命周期就开始了,当run抛出异常或阶段性关闭,线程就结束了。
线程的生命周期分为五个阶段:新建状态(New)、就绪状态(Runnable)、运行状态(Running)、阻塞状态(Blocked)、死亡状态(Terminated),线程的不同状态表明了线程当前正在运行的活动。
- 新建状态
JVM分配内存,没有任何线程特征。 - 就绪状态
当线程调用start()方法后,线程进入就绪状态,线程位于线程队列中,等待CPU分配。 - 运行状态
获得CPU使用权后执行线程执行体,该程序处于运行状态。系统剥夺CPU使用权后,处于等待状态,等待再次占有CPU,直至程序结束。 - 阻塞状态
运行的线程由于某种原因,被迫让出CPU使用权而进入阻塞状态。此时线程并没有进入等待对列,CPU由其他等待对列中的线程使用,直至解除阻塞状态,进入等待对列,等待系统分配CPU使用权后才能再次执行。
notify()
和·wait()
方法可以使线程进入阻塞状态。sleep()
方法使阻塞特定时间,join()
加入新进程会阻塞原进程,直至新进程运行完毕。
线程调度
程序的多个线程逻辑上使并发的,某个线程要想被执行必须获取CPU占有权,由Java虚拟机按特定的机制为线程分配CPU的使用权叫线程的调度。
线程调度由两种模型:分时调度和抢占式调度。分时调度使在实现线上,按相同维度的时间片轮流分配CPU使用权。抢占式调度是按优先级分配CPU使用权。
线程优先级
Java JVM默认是抢占式调度,优先级决定了CPU分配顺序。
图中大写的常量设置线程优先级。
线程休眠
sleep(long millis)
方法让线程休眠一段时间把CPU让给其他线程。该方法时静态方法,只能控制当前的线程休眠,休眠结束后,该线程进入等待对列。
线程让步
线程让步时正在执行的线程将CPU让给其他线程,yield()
方法。
线程插队
线程可以通过join()
方法在某个线程之前执行。
线程安全
多线程共享资源时由于延迟或其他原因导致的错误。
class MyRunnable implements Runnable{
private int total=5;
@Override
public void run() {
while (total>=1){
//获取当前线程
Thread thread=Thread.currentThread();
//获取线程名
String th_name=thread.getName();
//添加延迟
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(th_name+"正在发售第"+total--+"张票");
}
}
}
public class MainApp {
public static void main(String[] args) {
Thread myThreadOne1=new Thread(new MyRunnable());
Thread myThreadOne2=new Thread(new MyRunnable());
Thread myThreadOne3=new Thread(new MyRunnable());
myThreadOne1.start();
myThreadOne2.start();
myThreadOne3.start();
}
}
Java提供了同步机制,当多个线程使用同一个共享资源时将执行共享资源的代码放在synchronized
关键字修饰的代码块中,这个代码块被称作同步代码块。
synchronized 返回值类型 方法名([参数列表]){代码块}
synchronized (obj){
while (total>0){
//获取当前线程
Thread thread=Thread.currentThread();
//获取线程名
String th_name=thread.getName();
//添加延迟
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(th_name+"正在发售第"+total--+"张票");
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/156296.html