多线程
进程是一个应用程序(1个进程是一个软件),线程是一个进程中的执行场景/执行单元,一个进程可以启动多个线程。
进程可以看做是现实生活当中的公司,线程可以看做是公司当中的某个员工。
进程A和进程B的内存独立不共享
线程A和线程B堆内存和方法区内存共享,但是栈内存独立,一个线程一个栈。假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,
互不干扰,各自执行各自的,这就是多线程并发。
火车站,可以看做是一个进程,火车站中的每一个售票窗口可以看做是一个线程,我在窗口1购票,你可以在窗口2购票,你不需要等我,我也不需要等你,所以多线程并发可以提高效率。
使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束,main方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在压栈弹栈。
java中实现线程有三种方式
第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法
public class Test01 {
public static void main(String[] args) {
MyThread m = new MyThread();
// 不会启动线程,不会分配新的分支栈(这种方式是单线程)
// m.run();
// 启动线程
// start()方法的作用是在jvm中开辟一个新的栈空间,只要栈空间开出来了,start方法就结束了,线程就启动成功了
// 启动的线程会自动调用run()方法,并且run方法在分支栈的底部,main方法在主栈的底部,run方法和main方法是平级的,
m.start();
// 下面的代码还是运行在主线程中
for (int i = 0; i < 1000; i++) {
System.out.println("主线程" + i);
}
}
}
class MyThread extends Thread {
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("分支线程" + i);
}
}
}
第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法
建议使用接口方式,因为实现这个接口还可以去继承其他类,第一种方式继承Thread类之后就不能继承其他类了
public class Test02 {
public static void main(String[] args) {
/* // 创建一个可运行的对象
MyRunnable m = new MyRunnable();
// 将可运行的对象封装成一个线程
Thread t = new Thread(m);*/
// 合并代码
Thread t = new Thread(new MyRunnable());
// 启动线程
t.start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程" + i);
}
}
}
class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("分支线程" + i);
}
}
}
第二种方式:编写一个类,实现java.util.concurrent.Callable接口
优点:可以获取线程返回的结果
缺点:在获取线程结果时,会导致当前线程阻塞
public class Test10 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable m = new MyCallable();
FutureTask task = new FutureTask(m);
Thread t1 = new Thread(task);
t1.start();
// 获取call方法的返回值
// get()方法会导致main线程阻塞
Object o = task.get();
System.out.println(o); // 5秒后才输出300
}
}
class MyCallable implements Callable {
@Override
public Object call() throws Exception {
Thread.sleep(5000);
return 300;
}
}
线程生命周期
获取和修改线程的名字,休眠线程
public class Test03 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable2());
System.out.println(t1.getName()); // Thread-0
t1.setName("t1");
System.out.println(t1.getName()); // t1
Thread t2 = new Thread(new MyRunnable2());
System.out.println(t2.getName()); // Thread-1
// 获取当前线程对象
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName()); // main
try {
// 当前线程休眠
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello"); // hello会在5秒后才输出
}
}
class MyRunnable2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("分支线程" + i);
System.out.println(Thread.currentThread().getName());
}
}
}
打断线程的休眠
public class Test04 {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable3());
t.start();
// 希望5秒之后t线程醒来
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 终端t线程的睡眠,这种终断睡眠依靠java的异常机制
t.interrupt();
}
}
class MyRunnable3 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "start");
try {
Thread.sleep(1000 * 60 * 60);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "end");
}
}
合理终止线程的睡眠
打一个布尔标记
public class Test05 {
public static void main(String[] args) {
MyRunnable4 r = new MyRunnable4();
Thread t = new Thread(r);
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// t.stop() 不建议使用,会直接杀死进程,导致数据未保存
r.run = false;
}
}
class MyRunnable4 implements Runnable {
boolean run = true;
public void run() {
for (int i = 0; i < 10; i++) {
if (run) {
System.out.println(Thread.currentThread