java并发基础

1.基础

1.1线程状态

在这里插入图片描述
New: 这个状态呢,就是线程对象创建之后、启动之前,就是这个状态。
用代码来说呢,就是

Thread t = new Thread();
t.getState(); // New

Runnable: 当调用start方法后呢,线程就会进入Runnable状态,表示,我这个线程可以被执行了,如果调度器给这个线程分配了CPU时间,那么这个线程就可以被执行,这里一定要正确区分一下Runnable不是说正在执行,而是可以被执行,这两个还是有区别的。

Thread t = new Thread();
t.getState(); // New
t.start(); // Runnable

Blocked: 这个状态,当线程要进入临界区的时候,会发生。如果有一个线程已经到了获取锁进入了临界区,那么其他线程到了临界区会进入Blocked状态,由调度器选一个来执行,如果这个线程执行完毕后,大家还是一同为Blocked状态,调度器再选一个来执行。

Thread t = new Thread(new Runnable {
    void run() {
        synchronized (lock) { // <3> Blocked
            // dothings
        } 
    }
});
t.getState(); // <1> New

t.start(); // <2> Runnable

Waiting: 当调用了wait,join方法后,就会进入这个状态。一旦进入到这个状态,CPU就不会管你了,直到有别的线程通过notify方法将它唤起,否则的话,就会一直在等待中。其主要作用是需要等待其他线程执行完,满足了一定条件才可以继续往下执行,所以不会浪费 cpu 资源。

Thread t = new Thread(new Runnable {
    void run() {
        synchronized (lock) { // Blocked
            // dothings
            while (!condition) {
                lock.wait(); // <3> Waiting
            }
        } 
    }
});
t.getState(); // <1> New

t.start(); // <2> Runnable

Timed_Waiting: 这个状态也是等待,但是是有一个计时器在里面,最常见的是使用 Thread.sleep 方法触发,触发后,线程就进入了 Timed_waiting 状态,随后会由计时器触发,再进入Runnable状态。

Thread t = new Thread(new Runnable {
    void run() {
        Thread.sleep(1000); // Timed_waiting
    }
});
t.getState(); // New

t.start(); // Runnable

Terminated: 终结状态,当线程的所有代码都被执行完毕后,会进入到这个状态,这个就是字面意思了。

2.API

线程的新建

Thread t = new Thread();

run()

Thread t = new Thread();
t.run();

不能使用上面的方式启动线程,这样调用不会产生任何新的线程,只是一个单纯的方法调用。要想开启线程,不能使用run(),而应该用start()。

start()

new Thread(new Runnale() {
    @Override
    public void run() {}
}).start();

产生一个新线程去执行 run() 方法,只能调用一次,如果第二次调用,会抛出异常
线程的终止
stop()方法,但是已经被废弃。该方法太过暴力,强行把执行到一半的线程终止掉,可能会引起数据不一致的问题。stop()方法会直接终止线程,并立即释放这个线程持有的锁,而锁恰好是用来维持对象的一致性的。

那么要如何终止线程呢?可以自定义退出线程的方法,比如设置标志位

volatile boolean stoped;
public void myStop() {
    stoped = true;
}

在线程合适的地方, 通过判断stoped变量来手动退出线程。

@Override
public void run() {
    while (true) {
        if (stoped) {
            break;
        }
    }
    // synchronized (object) {do sth.}
}

线程的中断
线程中断不会使线程立即退出,而是通知目标线程“你应该退出了”,退不退出是由目标线程来决定的。

设置中断
Thread类的实例通过 interrupt() 方法,它通知目标线程中断,也就是设置中断标志位。
检查是否中断(但同时会清除当前线程的中断标志位 )

Thread类的实例通过 isInterrupt() 方法,用于判断当前线程是否被中断(通过检查中断标志位);

静态方法 Thread.interrupted() 也可以判断当前线程的中断状态

wait() 和 notify()

wait() 是 Object的方法,也就是说每个对象都能调用。在当前线程中某个对象调用了 wait() 方法则该线程就停止执行,转为等待状态,并加入对象 obj 的等待队列,一直等到其他线程调用了obj.notify(),才从等待队列移出,重新尝试获取锁。

Object类还有一个notifyAll()方法,会唤醒某对象等待队列中的所有线程。

notify() 和 wait()方法在执行前必须获得对象的锁。为此在synchronized (obj)中先获得了锁,才能调用wait()或者notify()方法

线程的挂起和继续执行

Thread类中还有两个已经被废弃的方法,分别是suspend()和resume()。

suspend()在导致线程暂停的同时,不会释放任何锁,而且线程状态还是Runnable,其他任何线程都得不到被该线程占用的锁,直到在线程上执行了resume(),被挂起的线程才能继续,其他阻塞在相关锁上的线程也才可以继续执行。

但是如如resume()不小心在suspend()之前调用了,可能造成线程被永久挂起,从而永久占用锁。

注意和notify()、wait()方法的区别。由于方法已被废弃,不再讨论更多。

join()
join()表示将线程“加入”到当前线程中,会一直阻塞当前线程,直到该线程执行完毕,或者说:当前线程一直等待join入的线程执行完毕后,才能继续执行。

public class Abc {
    public static volatile int i;
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {for (i = 0; i < 100; i++);});
        t.start();
        t.join();
        System.out.println(i);
    }
}

举个例子,上面的代码中有main线程和t线程,由于线程 t 的 join ,使得 main 线程中System.out.println(i)的一定是等待t线程执行完毕后才能执行,因此打印值一定是100 .

join() 的实现依赖于 wait()。A线程中调用了B线程的 join 方法,则相当于A线程调用了B线程的 wait 方法,在调用了B线程的 wait 方法后,A线程就会进入阻塞状态。所以 A 线程会等待 B 执行完毕后才能继续执行。

yield()

yield()表示主动礼让当前线程的CPU使用权,让出不是会不会回来参与CPU资源的争夺,下次还能被分配到。调用该方法,好比是当前线程说:我的工作暂时做完,给其他线程一些工作机会。

3.其他概念

守护线程

守护线程用于守护用户进程。守护进程在后台默默完成一些系统性的服务,比如垃圾回收线程、JIT 线程就可理解成守护线程。

而用户线程是用于完成程序的业务操作的,当程序中的所有用户线程执行完毕,守护线程没有了可守护的对象,程序自然就退出。

即:在一个Java程序中只有守护线程时,虚拟机就自然退出。

public static void main(String[] args) {
    Thread t = new Thread(() -> {
        while (true) {
        }
    });
    t.setDaemon(true);
    t.start();
    System.out.println("over");
}

在上面的代码中,线程 t 被设置成守护线程(必须在start()之前调用),用户线程 main 在打印 “over” 后执行完毕,t 没有要守护的线程了,所以程序会出,如果不设置t为守护线程,我们知道由于while (true)程序永远不会退出。

线程优先级
Java 的线程有 优先级,高优先级线程在竞争资源时更有优势,但这也是个概率问题,高优先级也可能抢占失败,低优先级线程也不是说一定会饥饿。

Java中的线程有1~10的优先级,数字越大,优先级越高。Thread类中内置了三个默认的优先级,如下。

    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值