创建进程
// 方法一: 从Thread派生一个自定义类,然后重写run()方法public class ThreadDemo{public static void main(String[] args) {Thread t = new MyThread();t.start(); // 启动新进程}}class MyThread extends Thread {@Overridepublic 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 {@Overridepublic 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,失败,等待中。两个线程都无法获取对方的锁,而导致死锁问题*/
