Java中的线程
-------- 线程相关概念
进程 (Process):是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位。可以把进程简单地理解为正在操作系统中运行的一个程序。
线程 (Thread):是进程的一个执行单元。一个线程就是进程中一个单一顺序的控制流,是进程的一个执行分支。进程是线程的容器,一个进程有至少一个或多个线程。
在操作系统中是以进程为单位分配资源,如CPU、虚拟存储空间等。多个线程共享同一个进程的资源。每个线程都有各自的线程栈,自己的寄存器环境,自己的线程本地存储。
主线程:JVM启动时会创建一个主线程,该主线程负责执行main方法。主线程就是运行main方法的线程。
Java中的线程不是孤立的,线程之间存在一些联系。如果在A线程中创建了B线程,称B线程为A线程的子线程,相应的A线程就是B线程的父线程。
串行,并发与并行:串行是指各个任务依次执行,并发是指在某段时间内交替完成多个任务;并行是同时完成多个任务。
并发可以提高事物的处理效率,即一段时间内可以处理或者完成更多的事情。并行是一种更为严格,理想的并发。
从硬件角度来说,如果是单核CPU,一个处理器一次只能执行一个线程的情况下,处理器可以使用时间片轮转技术,可以让CPU快速的在各个线程之间进行切换,对于用户来说,感觉是三个线程在同时执行。如果是多核心CPU,可以为不同的线程分配不同的CPU内核。
-------- 线程的创建与启动
在Java中,创建一个线程就是创建一个 Thread 类(子类)的对象(实例)。
Thread 类有两个常用的构造方法:Thread() 与 Thread(Runnable)。对应的创建线程的两种方式:
- 定义 Thread 类的子类,即继承 Thread 类。
- 定义一个 Runnable 接口的实现类(常用)
这两种创建线程的方式没有本质的区别。因为Java中的类只允许单继承,当线程类继承了 Thread 后就不能再继承其他类了,所以开发中经常使用实现 Runnable 接口的形式创建线程,这样 Runnable 实现类还可以继承其他类,使用更灵活。
// 方式一:定义一个类继承 Thread类 public class MyThread extends Thread { //2) 重写Thread父类中的run(),run()方法体中的代码就是子线程要执行的任务 @Override public void run() { System.out.println("这是子线程打印的内容"); } } // 测试类 public class Test { public static void main(String[] args) { System.out.println("JVM启动main线程,main线程执行main方法"); //3)创建子线程对象 MyThread thread = new MyThread(); //4)启动线程 thread.start(); /* 调用线程的start()方法来启动线程, 实质就是请求JVM运行相应的线程,这个线程具体在什么时候运行由线程调度器(Scheduler)决定。注意: start()方法调用结束并不意味着子线程开始运行 线程调度器选中执行线程时执行线程的run()方法 如果开启了多个线程,start()调用的顺序并不一定就是线程启动的顺序 多线程运行结果与代码执行顺序或调用顺序无关 */ System.out.println("main线程后面其他 的代码..."); } }
// 方式二:定义类实现Runnable接口 public class MyRunnable implements Runnable { //2) 重写Runnable接口中的抽象方法run(),run()方法就是子线程要执行的代码 @Override public void run() { for(int i = 1; i < 5; i++){ System.out.println( "子线程1:" + i); } } } // 测试类 public class Test { public static void main(String[] args) { //3) 创建Runnable接口的实现类对象 MyRunnable runnable = new MyRunnable(); //4)创建线程对象 Thread thread = new Thread(runnable); //5)开启线程 thread.start(); // 当前是main线程 for(int i = 1; i<5; i++){ System.out.println( "main==> " + i); } // 有时调用Thread(Runnable)构造方法时,实参也会传递匿名内部类对象 Thread thread2 = new Thread(new Runnable() { @Override public void run() { for(int i = 1; i<5; i++){ System.out.println( "子线程2:" + i); } } }); thread2.start(); } }
-------- 线程的常用方法
- static Thread currentThread() 返回对当前正在执行的线程对象的引用。
- void setName(String name) 改变线程名称,使之与参数 name 相同。
- String getName() 返回该线程的名称。从Thread - 0 开始,编号依次累加
- boolean isAlive() 测试线程是否处于活动状态。
- void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
设置线程对象名称的两种方式:
- Thread(String name) 子类需要提供String的带参构造
- void setName(String name) 改变线程名称,使之与参数 name 相同。
// 对应第一种创建线程的方式
public class MyThread extends Thread { public MyThread() { // 无参构造 super(); } public MyThread(String name) { // 有参构造 super(name); } @Override public void run() { //谁调用run方法,谁就是this, this可以省略 System.out.println(this.getName() + " 调用了run()方法"); } } // 测试类 public class Test { public static void main(String[] args) { // 方式一 MyThread thread = new MyThread("线程一"); System.out.println(thread.getName()); // 线程一 thread.start(); // 方式二 MyThread thread2 = new MyThread(); thread2.setName("线程2"); System.out.println(thread2.getName()); // 线程2 thread2.start(); } }
// 对应第二种创建线程的方式
public class MyRunnable implements Runnable { @Override public void run() { Thread thread = Thread.currentThread(); //1)获取当前正在执行的线程对象的引用 System.out.println(thread.getName() + " 调用了run()方法"); } } // 测试类 public class Test { public static void main(String[] args) {
//2) 创建实现类对象 MyRunnable runnable = new MyRunnable(); //3)创建线程对象 Thread(Runnable target, String name)方法(常用) Thread t1 = new Thread(runnable,"线程一"); Thread t2 = new Thread(runnable, "t2");
//4)开启线程 t1.start(); t2.start(); } }