线程和进程区别
进程:是系统进行分配和管理资源的基本单位
线程:进程的一个执行单元,是进程内调度的实体、是CPU调度和分派的基本单位,是比进程更小的独立运行的基本单位。线程也被称为轻量级进程,线程是程序执行的最小单位。
一个程序至少一个进程,一个进程至少一个线程。
main函数演示
public class Demo {
public static void main(String [] args) {
System.out.println("123");
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
}
可以看到一个进程必然包含一个线程
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。 而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式进行。
如何处理好同步与互斥是编写多线程程序的难点。 多进程程序更健壮,进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响, 而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,所以可能一个线程出现问题,进而导致整个程序出现问题
线程的状态及相互转换
查看Thread源码
查看类结构 State类定义
初始(NEW): 新创建了一个线程对象,但还没有调用start()方法。
运行(RUNNABLE): 处于可运行状态的线程正在JVM中执行,但它可能正在等待来自操作系统的其他资源,例如处理器。 Thread.start()会进入RUNNABLE等待状态,CPU为没和线程分配时间片,可能时间片还没分配过来,还没有执行真正的run方法
public class ThreadStateDemo {
public static void main(String [] args) throws InterruptedException {
//RUNNABLE 运行
Thread thread =new Thread(()->{
try {
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
});
thread.start();
}
}
阻塞(BLOCKED): 线程阻塞于synchronized锁,等待获取synchronized锁的状态。
public class ThreadStateDemo {
public static void main(String [] args) throws InterruptedException {
//BLOCKED 阻塞
Object obj = new Object();
Thread thread =new Thread(()->{
synchronized (obj){
try {
//获取锁后一直休眠
Thread.sleep(100000000000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
Thread.sleep(2000L);
Thread thread2 =new Thread(()->{
synchronized (obj){
}
});
thread2.start();
}
}
等待(WAITING): Object.wait()、join()、 LockSupport.park(),进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
public class ThreadStateDemo {
public static void main(String [] args) throws InterruptedException {
//WAITING等待
Object obj = new Object();
Thread thread =new Thread(()->{
synchronized (obj){
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
}
超市等待(TIME_WAITING): Object.wait(long)、Thread.join()、LockSupport.parkNanos()、LockSupport.parkUntil,该状态不同于WAITING,它可以在指定的时间内自行返回。
终止(TERMINATED): 表示该线程已经执行完毕。
线程状态转换图
创建线程的方式
方式一:继承Thread 并重写类的run方法
/**
* 创建线程方式一
*/
public class MyThread extends Thread {
/**
* 打印当前线程名称
*/
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String [] args){
MyThread myThread = new MyThread();
myThread.setName("线程Demo1");
//启动线程
myThread.start();
}
}
方式二:实现Runable接口,并实现run方法
/**
* 创建线程方式二
*/
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String [] args){
Thread thread = new Thread(new MyRunnable());
thread.setName("线程demo2");
thread.start();
}
}
方式三:匿名内部类
/**
* 匿名内部类创建线程
*/
public class MyThread {
public static void main(String [] args){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
thread.start();
}
}
方式四:Lambda表达式
JDK8的语法
public class Lambda {
public static void main(String [] args){
new Thread(() ->{
System.out.println(Thread.currentThread().getName());
}).start();
}
}
方式五:线程池创建
虽然运行结束,但程序继续执行
/**
* 线程池创建线程
*/
public class ThreadPool {
public static void main(String [] args){
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
}
实际开发中,选第二种:java只允许单继承 但你可以实现多个接口
使用第二种可以增加程序的健壮性,可以被多个程序共享,代码跟数据相对独立
调用start()和run()有什么区别?
1.调用start()方法:
通知“线程规划器”当前线程已经准备就绪,等待调用线程对象的run()方法。这个过程就是让系统安排一个时间来调用Thread中的run()方法,使线程得到运行,启动线程,具有异步执行的效果。
调用start()方法,也就是线程状态转变成可运行状态的过程。
2.调用run()方法:
不是异步执行,而是同步执行,当前线程并不交给“线程规划器”来处理,而是由main主线程来调用run()方法,也就是必须等run()方法中的代码执行完后才可以执行后面的代码。
可以用上面代码测试一下,将start改为run输出的是main 而不是命名的线程