线程基础

103 阅读4分钟

线程的两种创建方式

1、继承Thread类,重写 run 方法

class Thread01 extends Thread {
    @Override
    public void run() {
        int count = 0;
        while(true) {
            System.out.println("继承Thread线程正在运行:" + Thread.currentThread().getName() + "===" + ++count);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count == 100) {
                break;
            }
        }
    }
}

2、实现Runnable接口,重写 run 方法

class Thread02 implements Runnable {

    @Override
    public void run() {
        int count = 0;
        while(true) {
            System.out.println("实现Runnable线程正在运行:" + Thread.currentThread().getName() + "===" + ++count);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count == 100) {
                break;
            }
        }
    }
}

JConsole监控线程执行情况

在控制台输入 JConsole,打开Java监视和管理控制台

image.png

线程终止

1、当线程完成任务后,会自动退出

2、还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式

在线程中设置变量,通过控制变量来实现通知方式

class Thread03 extends Thread {
    private boolean loop = true;
    @Override
    public void run() {
        int count = 0;
        while(loop) {
            System.out.println("继承Thread线程正在运行:" + Thread.currentThread().getName() + "===" + ++count);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count == 100) {
                break;
            }
        }
    }

    public void setLoop(boolean loop) {
        // 把传入的参数赋值给当前对象的loop属性
        this.loop = loop;
    }
}

线程常用方法

  1. setName 设置线程名称,使之与参数name相同
  2. getName 返回该线程的名称
  3. start 使该线程开始运行,Java 虚拟机底层调用该线程的start0 方法
  4. run 调用线程对象 run 方法
  5. setPriority 更改线程的优先级
  6. getPriority 获取线程的优先级
  7. sleep 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
  8. interrupt 中断线程
  9. yield 线程的礼让。让出CPU,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
  10. join 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务。

用户线程和守护线程

  1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
  2. 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。设置为守护线程的方法:setDaemon(true)
  3. 常见的守护线程:垃圾回收机制

线程同步(Synchronized)

1、同步代码块

synchronized(对象) {  // 得到对象的锁,才能操作同步代码
    // 需要被同步的代码
}

2、synchronized 还可以放在方法声明中,表示整个方法-为同步方法

public synchronized void fun(String str) {
    // 需要被同步的代码
}

互斥锁

  1. Java在Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
  2. 每个对象都对应与一个可成为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
  3. 关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表名该对象在任一时刻只能由一个线程访问
  4. 同步的局限性:导致程序的执行效率要降低
  5. 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一对象)
  6. 同步方法(静态的)的锁为当前类本身

注意事项

  1. 同步方法如果是非静态方法,默认锁对象为this
  2. 同步方法如果是静态方法,默认锁对象为当前类(当前类.class)
  3. 多个线程的锁对象需为同一对象

线程的死锁

基本介绍

两个或多个线程互相持有对方所需要的锁资源,且都在等待其他线程释放锁资源,从而导致了死锁。
如下代码,就会出现死锁情况:

public class DeaLockDemo {

    private static Object lock1 = new Object();
    private static Object lock2 = new Object();

    public static class ThreadA implements Runnable{

        @Override
        public void run() {
            synchronized (lock1){
                System.out.println("ThreadA 获得 lock1 锁资源");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("ThreadA 尝试获取 lock2 锁资源");
                synchronized (lock2){
                    System.out.println("ThreadA 获得了 lock1 和 lock2 锁资源");
                }
            }
        }
    }

    public static class ThreadB implements Runnable{

        @Override
        public void run() {
            synchronized (lock2) {
                System.out.println("ThreadB 获得 lock2 锁资源");
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("ThreadB 尝试获取 lock1 锁资源");
                synchronized (lock1){
                    System.out.println("ThreadB 获得了 lock1 和 lock2 锁资源");
                }
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new ThreadA(), "ThreadA").start();
        new Thread(new ThreadB(), "ThreadB").start();
    }
}

释放锁

  1. 当前线程的同步方法或同步代码块执行结束
  2. 当前线程的同步代方法或同步代码块遇到break、return
  3. 当前线程在同步方法或同步代码块中出现了未处理的异常,导致异常结束
  4. 当前线程在同步方法或同步代码块中,执行了线程对象的wait()方法,当前线程暂停,且释放锁