创建进程
// 方法一: 从Thread派生一个自定义类,然后重写run()方法
public class ThreadDemo{
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实例
public class ThreadDemo{
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!");
}
}
// 方式三:用java8引入的lambda语法进一步简写为
public class ThreadDemo{
public static void main(String[] args) {
Thread t = new Thread(()->{
System.out.println("start new thread!");
});
t.start(); // 启动新进程
}
}
线程的状态
java线程的状态有以下几种:
- New:新创建的线程,尚未执行;
- Runnable:运行中的线程,正在执行
run()
方法的Java代码; - Blocked:运行中的线程,因为某些操作被阻塞而挂起;
- Waiting:运行中的线程,因为某些操作在等待中;
- Timed Waiting:运行中的线程,因为执行
sleep()
方法正在计时等待; - Terminated:线程已终止,因为
run()
方法执行完毕。
注意:一个线程对象只能调用一次start()方法启动新线程
线程终止的原因有:
- 线程正常终止:
run()
方法执行到return
语句返回; - 线程意外终止:
run()
方法因为未捕获的异常导致线程终止; - 对某个线程的
Thread
实例调用stop()
方法强制终止(强烈不推荐使用)。
public class ThreadDemo{
// 需要抛出 InterruptedException错误
public static void main(String[] args) throws InterruptedException{
Thread t = new Thread(() -> {
System.out.println("hello");
});
System.out.println("start");
t.start();
t.join(); // 等待t进程执行完毕之后,再执行下面的
System.out.println("end");
}
}
中断进程
// 中断线程
// 方式二:在其他线程中对目标线程调用interrupt()方法,调用线程对象.interrupt()中断该线程。
public class ThreadDemo{
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!");
}
}
}
// 方式二:设置标志位,用running标志位来标识线程是否应该继续运行
public class ThreadDemo{
public static void main(String[] args) throws InterruptedException{
MyThread t = new MyThread();
t.start();
Thread.sleep(1);
t.running = false;
}
}
class MyThread extends Thread{
public volatile boolean running = true;
public void run(){
int n = 0;
while (running) {
n++;
System.out.println(n + " hello!");
}
System.out.println("end!");
}
}
/*
volatile关键字的目的是告诉虚拟机:
每次访问变量时,总是获取主内存的最新值;
每次修改变量后,立刻回写到主内存。
*/
守护线程
// 守护进程就是:主进程结束被守护的进程无论执行没有执行都会结束
t.setDaemon(true); // 守护进程一定要写在启动进程前面,不然报错
t.start();
线程同步与方法
// 线程同步
// Java程序使用synchronized关键字对一个对象进行加锁
public class ThreadDemo{
public static void main(String[] args) throws InterruptedException{
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;
}
}
}
}
// 线程同步方法
public class Counter {
private int count = 0;
public void add(int n) {
synchronized(this) {
count += n;
}
}
public void dec(int n) {
synchronized(this) {
count -= n;
}
}
public int get() {
return count;
}
}
/*
用synchronized修饰方法可以把整个方法变为同步代码块,synchronized方法加锁对象是this,this当前操作对象的实例
通过合理的设计和数据封装可以让一个类变为“线程安全”
一个类没有特殊说明,默认不是thread-safe
*/
死锁
// java的线程锁是可重入的锁
// 什么是可重入锁?
// JVM允许同一个线程重复获取同一个锁,这种能被同一个线程反复获取的锁,就叫做可重入锁。
// 由于java的线程锁是可重入锁,所以,获取锁的时候,不但要判断是否是第一次获取,还要记录这是第几次获取。没获取一次锁,记录+1,每退出synchronized块,记录-1,减到0的时候,才会真正释放锁。
// 死锁就是:
/*
线程1:进入add(),获得lockA;
线程2:进入dec(),获得lockB。
随后:
线程1:准备获得lockB,失败,等待中;
线程2:准备获得lockA,失败,等待中。
两个线程都无法获取对方的锁,而导致死锁问题
*/