多线程
启动 一个java程序 == 启动一个JVM进程,从而JVM的主线程调用main方法,同时main方法可以调用其他的线程
创建线程的方法
Thread t = new Thread();//创建一个新线程
t.start();//启动线程
-
创建一个类继承Thread类,并重写run方法(start方法内部调用了run方法)
public class Main { public static void main(String[] args) { Thread t = new MyThread(); t.start(); // 启动新线程 } } class MyThread extends Thread { @Override public void run() { System.out.println("start new thread!"); } }
-
创建Thread实例时传入Runnable实例(这个实例实现了Runnable接口,重写了run方法)
public class Main { public static void main(String[] args) { Thread t = new Thread(new MyRunnable()); t.start(); // 启动新线程 } } class MyRunnable implements Runnable { @Override public void run() { System.out.println("start new thread!"); } }
多线程的使用细节
public class Main {
public static void main(String[] args) {
System.out.println("main start...");//1
Thread t = new Thread() {//2
public void run() {
System.out.println("thread run...");//5
System.out.println("thread end.");//6
}
};
t.start();//3
System.out.println("main end...");//4
}
}
main线程执行了1,2,3,4语句,但是当**t.start()**执行时,run()方法和4语句是并发执行,且由操作系统调度,所以4,5,6的执行顺序无法预知
可以用Thread.sleep()方法强迫当前线程暂停一段时间,从而实现线程的同步和互斥
public class Main {
public static void main(String[] args) {
System.out.println("main start...");//1
Thread t = new Thread() {
public void run() {
System.out.println("thread run...");//2
try {
Thread.sleep(10);
} catch (InterruptedException e) {}
System.out.println("thread end.");//3
}
};
t.start();
try {
Thread.sleep(20);
} catch (InterruptedException e) {}
System.out.println("main end...");//4
}
}
执行顺序为1->2->3->4,注意sleep()的单位是毫秒
Thread实例直接调用run()方法对多线程来言是无效的
public class Main {
public static void main(String[] args) {
Thread t = new MyThread();
t.run();
}
}
class MyThread extends Thread {
public void run() {
System.out.println("hello");
}
}
以上相当于在main方法中调用了MyThread实例的run方法,并没有启动新线程
必须使用Thread实例的start方法才能启动新线程
线程的状态
- New:新创建的线程,尚未执行;
- Runnable:运行中的线程,正在执行
run()
方法的Java代码; - Blocked:运行中的线程,因为某些操作被阻塞而挂起;
- Waiting:运行中的线程,因为某些操作在等待中;
- Timed Waiting:运行中的线程,因为执行
sleep()
方法正在计时等待; - Terminated:线程已终止,因为
run()
方法执行完毕。
可以调用join()方法,等待当前线程执行结束再往下执行
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
System.out.println("hello");
});
System.out.println("start");
t.start();
t.join();
System.out.println("end");
}
}
中断线程
对目标线程调用interrupt()方法,通知该线程中止(并不是立即中止)
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new MyThread();
t.start();
Thread.sleep(1); // 暂停1毫秒
t.interrupt(); // 中断t线程
t.join(); // 等待t线程结束
System.out.println("end");
}
}
class MyThread extends Thread {
public void run() {
int n = 0;
while (! isInterrupted()) {
n ++;
System.out.println(n + " hello!");
}
}
}
t.interrupt()改变了interrupted状态,故退出循环
执行t.join()使得main线程等待t线程结束,如果此时对main线程调用interrupt(),则join()方法会抛出InterruptedException异常。目标线程只要捕获到
join()
方法抛出的InterruptedException
,就说明有其他线程对其调用了interrupt()
方法,通常情况下该线程应该立刻结束运行
线程同步
解决多个线程同时读写共享变量时,出现数据不一致的问题
使用synchronized关键字对一个对象加锁,保证一段代码的原子性
synchronized保证代码块在任意时刻最多只有一个线程能执行
public class Main {
public static void main(String[] args) throws Exception {
var add = new AddThread();
var dec = new DecThread();
add.start();
dec.start();
add.join();
dec.join();
System.out.println(Counter.count);
}
}
class Counter {
public static final Object lock = new Object();
public static int count = 0;
}
class AddThread extends Thread {
public void run() {
for (int i=0; i<10000; i++) {
synchronized(Counter.lock) {
Counter.count += 1;
}
}
}
}
class DecThread extends Thread {
public void run() {
for (int i=0; i<10000; i++) {
synchronized(Counter.lock) {
Counter.count -= 1;
}
}
}
}
synchronized(Counter.lock) { // 获取锁
...
} // 释放锁
使用Counter.lock实例作为锁,必须先获得锁,才能进入代码块执行,执行后自动释放锁
synchronized的使用:
- 找出修改共享变量的线程代码块;
- 选择一个共享实例作为锁;
- 使用
synchronized(lockObject) { ... }