Java - JavaSE - 多线程
多线程
多线程创建的三种方式:
- 继承 Thread 类,重写 run 方法。
- 实现 Runnable 接口。
- 实现 Callable 接口。这个方法比较编写代码比较复杂,是 JDK 1.5 出来的。
方式 1 和 方式 2 的区别:
- extends 是继承,一般要增强类的功能使用,重写或添加方法。
- implements 是实现接口。
- 单继承和多实现。
Thread 类源码分析
从官方文档得到:
- JVM 是支持多线程的。
- 线程是有优先级的。当在线程中创建一个线程对象时,新对象的优先级和当前线程相同,当且仅当线程是守护线程,新的线程对象才能是守护线程。
- 创建线程的方式:继承 Thread 类;实现 Runnable 接口。
//继承 Thread 类
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
//给出调用方法
PrimeThread p = new PrimeThread(143);
p.start();
//实现 Runnable 接口
class PrimeRun implements Runnable {
long minPrime;
PrimeRun(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
//给出调用方法
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
从源码级别可以看到这两种创建线程方法的区别,一个是直接创建继承 Thread 类的子类对象,子类对象可以直接调用 start 方法;另一个是使用了 Thread 类不同的构造器,传入的对象是一个实现了 Runnable 接口的类对象。
Thread.State - Thread 的内部枚举类
Thread 类从源码可以看到,线程一共有六种状态,分别是:
public enum State {
//A thread that has not yet started is in this state.
//新建,刚刚 new 出来,还没有调用 start 方法
NEW,
//A thread executing in the Java virtual machine is in this state.
//线程在虚拟机中正在运行
RUNNABLE,
//A thread that is blocked waiting for a monitor lock is in this state.
//被锁住了
BLOCKED,
//A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
//处于等待状态
WAITING,
//A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
//确定时间的等待状态
TIMED_WAITING,
//A thread that has exited is in this state.
//线程已经退出,即终结了
TERMINATED;
}
感兴趣的同学可以再去看看源码。
synchronized
synchronized 简单解读
原理可以看这篇博客 https://www.cnblogs.com/paddix/p/5367116.html ,Object 类有一个隐式的 monitor,在调用 synchronized 修饰的方法时,会调用 monitorenter 指令,此时犹如房门上锁,房内存放的都是 synchronized 方法,这种加锁的方式很粗暴,业内称重量级锁。这种说法很容易理解,只需要选择一个方法使用却将很多同步方法一起锁住,妨碍了其他方法的调用,在并发编程中很少使用这种加锁方式。
Object 类的 notify notifyAll 和 wait 方法也需要 monitor 的作用,因此在使用这三个方法需要在 synchronized 修饰的方法或代码块下,由于三个方法都是 Object 类的 final 方法,意味着 Java 世界所有的对象都有这三个方法。
测试:不同线程调用同一对象的同步方法和非同步方法
public class SynchronizedTest01 {
public static void main(String[] args) {
Print print = new Print();
new Thread(() -> {
print.printA();
}, "A").start();
new Thread(() -> {
print.printB();
}, "B").start();
try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); }
new Thread(() -> {
print.printA();
}, "A").start();
new Thread(() -> {
print.printX();
}, "X").start();
}
}
class Print {
synchronized void printA() {
System.out.println("我是 " + Thread.currentThread().getName() + " 线程,我已经调用了 printA() ,但是我要休息 3s");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("A");
}
synchronized void printB() {
System.out.println("我是 " + Thread.currentThread().getName() + " 线程,我等了 A 线程 3s ,这货终于好了,我赶紧打印");
System.out.println("B");
}
void printX() {
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("我是 " + Thread.currentThread().getName() + " 线程,我看 A 线程要休息 3s ,我休息 1s 后赶紧打印");
System.out.println("X");
}
}
输出结果:对照 code 和输出的内容可以很好的理解。
我是 A 线程,我已经调用了 printA() ,但是我要休息 3s
A
我是 B 线程,我等了 A 线程 3s ,这货终于好了,我赶紧打印
B
我是 A 线程,我已经调用了 printA() ,但是我要休息 3s
我是 X 线程,我看 A 线程要休息 3s ,我休息 1s 后赶紧打印
X
A
结论:同一个对象,其同步方法不能同时调用,非同步方法不受此限制。