Java线程内容

chengdanally / 2024-09-20 / 原文

一:线程的创建

  1. 继承Thread类 重写run方法
public class MyTest {
    public static void main(String[] args) {
        MyJob mj = new MyJob();
        mj.start();
    }
}
class MyJob extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("MyJob:" + i);
        }
    }
}
  1. 实现Runnable接口 重写run方法(用的最多的)
public class MiTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable);
        t1.start();
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("MyRunnable:" + i);
        }
    }
}

最常用的方式是
匿名内部类方式:

Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("thread:" + i);
        }
    }
}).start();;

lambda方式:

Thread t2 = new Thread(() -> {
    for (int i = 0; i < 10; i++) {
        System.out.println("thread:" + i);
    }
}).start();
  1. 实现Callable接口 重写call方法,配合FutureTask
    特点:可以返回结果
public class MiTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        FutureTask futureTask = new FutureTask(myCallable);
        //创建Thread线程
        Thread t1 = new Thread(futureTask);
        //启动线程
        t1.start();
        //获取结果
        Object count = futureTask.get();
        System.out.println("返回结果:" + count);
    }
}

class MyCallable implements Callable{

    @Override
    public Object call() throws Exception {
        int count = 0;
        for (int i = 0; i < 10; i++) {
            count += i;
        }
        return count;
    }
}
  1. 基于线程池构建线程

二:Java中线程的6种状态

说明:
NEW(新建状态):Thread对象被创建出来了,但是还没有执行start方法。
RUNNABLE(就绪状态):Thread对象调用了start方法,就为RUNNABLE状态(CPU调度/没有调度)
BLOCKED(阻塞状态):synchronized没有拿到同步锁,被阻塞的情况
WAITING(等待状态):调用wait方法就会处于WAITING状态,需要被手动唤醒
TIME_WAITING(超时等待):调用sleep方法或者join方法,会被自动唤醒,无需手动唤醒
TERMINATED(结束状态):run方法执行完毕,线程生命周期到头了
BLOCKED、WAITING、TIME_WAITING:都可以理解为是阻塞、等待状态,因为处在这三种状态下,CPU不会调度当前线程

三:线程内容
线程的强占:
使用Thread类的非静态方法join(),join() 方法允许一个线程等待另一个线程完成
join方法可以指定等待时间,如果子线程在等待时间内结束,则主线程自动变为就绪状态

默认情况下,线程都是非守护线程
JVM会在程序中没有非守护线程时,结束掉当前JVM。主线程默认是非守护线程,如果主线程执行结束,需要查看当前JVM内是否还有非守护线程,如果没有JVM直接停止。当对象被回收的时候,会执行finalize方法,执行finalize方法的线程是守护线程,所以不一定会执行成功。

在调用wait方法和notify以及norifyAll方法时,必须在synchronized修饰的代码块或者方法内部才可以,因为要操作基于某个对象的锁的信息维护。
线程调用wait方法后,当前线程会释放锁资源

线程的结束方式:

  1. 让线程的run方法执行结束,无论是return结束,还是抛出异常结束,都可以的
  2. 使用interrupt方法
    使用共享变量方式,改变interrupt标记位,线程默认情况下标记位是false的(不常用)

通过打断WAITING或者TIMED_WAITING状态的线程,从而抛出异常自行处理

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        while(true){
            // 执行任务
            // 没有任务,让线程休眠
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("基于打断形式结束当前线程");
                return;
            }
        }
    });
    t1.start();
    Thread.sleep(500);
    t1.interrupt();
}

四、Java并发编程三大特性:
1> 原子性:原子性指一个操作是不可分割的,不可中断的,一个线程在执行时,另一个线程不会影响到他。
问:如何保证原子性呢?
使用synchronized可以让避免多线程同时操作临界资源,同一时间点,只会有一个线程正在操作临界资源
使用Lock锁,推荐ReentrantLock锁,性能会更好,ReentrantLock底层是基于AQS实现的,有一个基于CAS维护的state变量来实现锁的操作。
使用ThreadLocal,他保证原子性的方式,是不让多线程去操作临界资源,让每个线程去操作属于自己的数据

ThreadLocal内存泄漏问题:

  • 如果ThreadLocal引用丢失,key因为弱引用会被GC回收掉,如果同时线程还没有被回收,就会导致内存泄漏,内存中的value无法被回收,同时也无法被获取到。
  • 只需要在使用完毕ThreadLocal对象之后,及时的调用remove方法,移除Entry即可

2> 可见性
3> 有序性