创建进程

  1. // 方法一: 从Thread派生一个自定义类,然后重写run()方法
  2. public class ThreadDemo{
  3. public static void main(String[] args) {
  4. Thread t = new MyThread();
  5. t.start(); // 启动新进程
  6. }
  7. }
  8. class MyThread extends Thread {
  9. @Override
  10. public void run(){
  11. System.out.println("start new thread!");
  12. }
  13. }
  14. // 方式二:创建Thread实例时,传入一个Runnable实例
  15. public class ThreadDemo{
  16. public static void main(String[] args) {
  17. Thread t = new Thread(new MyRunnable());
  18. t.start(); // 启动新进程
  19. }
  20. }
  21. class MyRunnable implements Runnable {
  22. @Override
  23. public void run(){
  24. System.out.println("start new thread!");
  25. }
  26. }
  27. // 方式三:用java8引入的lambda语法进一步简写为
  28. public class ThreadDemo{
  29. public static void main(String[] args) {
  30. Thread t = new Thread(()->{
  31. System.out.println("start new thread!");
  32. });
  33. t.start(); // 启动新进程
  34. }
  35. }

线程的状态

java线程的状态有以下几种:

  • New:新创建的线程,尚未执行;
  • Runnable:运行中的线程,正在执行run()方法的Java代码;
  • Blocked:运行中的线程,因为某些操作被阻塞而挂起;
  • Waiting:运行中的线程,因为某些操作在等待中;
  • Timed Waiting:运行中的线程,因为执行sleep()方法正在计时等待;
  • Terminated:线程已终止,因为run()方法执行完毕。

注意:一个线程对象只能调用一次start()方法启动新线程

线程终止的原因有:

  • 线程正常终止:run()方法执行到return语句返回;
  • 线程意外终止:run()方法因为未捕获的异常导致线程终止;
  • 对某个线程的Thread实例调用stop()方法强制终止(强烈不推荐使用)。
  1. public class ThreadDemo{
  2. // 需要抛出 InterruptedException错误
  3. public static void main(String[] args) throws InterruptedException{
  4. Thread t = new Thread(() -> {
  5. System.out.println("hello");
  6. });
  7. System.out.println("start");
  8. t.start();
  9. t.join(); // 等待t进程执行完毕之后,再执行下面的
  10. System.out.println("end");
  11. }
  12. }

中断进程

  1. // 中断线程
  2. // 方式二:在其他线程中对目标线程调用interrupt()方法,调用线程对象.interrupt()中断该线程。
  3. public class ThreadDemo{
  4. public static void main(String[] args) throws InterruptedException{
  5. Thread t = new MyThread();
  6. t.start();
  7. Thread.sleep(1); // 暂停1毫秒
  8. t.interrupt(); // 中断t线程
  9. t.join(); // 等待t线程结束
  10. System.out.println("end");
  11. }
  12. }
  13. class MyThread extends Thread{
  14. public void run(){
  15. int n = 0;
  16. while (!isInterrupted()){
  17. n++;
  18. System.out.println(n + " hello!");
  19. }
  20. }
  21. }
  22. // 方式二:设置标志位,用running标志位来标识线程是否应该继续运行
  23. public class ThreadDemo{
  24. public static void main(String[] args) throws InterruptedException{
  25. MyThread t = new MyThread();
  26. t.start();
  27. Thread.sleep(1);
  28. t.running = false;
  29. }
  30. }
  31. class MyThread extends Thread{
  32. public volatile boolean running = true;
  33. public void run(){
  34. int n = 0;
  35. while (running) {
  36. n++;
  37. System.out.println(n + " hello!");
  38. }
  39. System.out.println("end!");
  40. }
  41. }
  42. /*
  43. volatile关键字的目的是告诉虚拟机:
  44. 每次访问变量时,总是获取主内存的最新值;
  45. 每次修改变量后,立刻回写到主内存。
  46. */

守护线程

  1. // 守护进程就是:主进程结束被守护的进程无论执行没有执行都会结束
  2. t.setDaemon(true); // 守护进程一定要写在启动进程前面,不然报错
  3. t.start();

线程同步与方法

  1. // 线程同步
  2. // Java程序使用synchronized关键字对一个对象进行加锁
  3. public class ThreadDemo{
  4. public static void main(String[] args) throws InterruptedException{
  5. var add = new AddThread();
  6. var dec = new DecThread();
  7. add.start();
  8. dec.start();
  9. add.join();
  10. dec.join();
  11. System.out.println(Counter.count);
  12. }
  13. }
  14. class Counter{
  15. public static final Object lock = new Object();
  16. public static int count = 0;
  17. }
  18. class AddThread extends Thread{
  19. public void run(){
  20. for (int i=0;i<10000;i++){
  21. synchronized (Counter.lock){ // 获取锁
  22. Counter.count += 1;
  23. } // 释放锁
  24. }
  25. }
  26. }
  27. class DecThread extends Thread{
  28. public void run(){
  29. for (int i=0;i<10000;i++){
  30. synchronized (Counter.lock){
  31. Counter.count -= 1;
  32. }
  33. }
  34. }
  35. }
  36. // 线程同步方法
  37. public class Counter {
  38. private int count = 0;
  39. public void add(int n) {
  40. synchronized(this) {
  41. count += n;
  42. }
  43. }
  44. public void dec(int n) {
  45. synchronized(this) {
  46. count -= n;
  47. }
  48. }
  49. public int get() {
  50. return count;
  51. }
  52. }
  53. /*
  54. 用synchronized修饰方法可以把整个方法变为同步代码块,synchronized方法加锁对象是this,this当前操作对象的实例
  55. 通过合理的设计和数据封装可以让一个类变为“线程安全”
  56. 一个类没有特殊说明,默认不是thread-safe
  57. */

死锁

  1. // java的线程锁是可重入的锁
  2. // 什么是可重入锁?
  3. // JVM允许同一个线程重复获取同一个锁,这种能被同一个线程反复获取的锁,就叫做可重入锁。
  4. // 由于java的线程锁是可重入锁,所以,获取锁的时候,不但要判断是否是第一次获取,还要记录这是第几次获取。没获取一次锁,记录+1,每退出synchronized块,记录-1,减到0的时候,才会真正释放锁。
  5. // 死锁就是:
  6. /*
  7. 线程1:进入add(),获得lockA;
  8. 线程2:进入dec(),获得lockB。
  9. 随后:
  10. 线程1:准备获得lockB,失败,等待中;
  11. 线程2:准备获得lockA,失败,等待中。
  12. 两个线程都无法获取对方的锁,而导致死锁问题
  13. */