线程同步

JVM线程状态

image.png

public enum State {
        NEW,
        RUNNABLE,
        BLOCKED,
        WAITING,
        TIMED_WAITING,
        TERMINATED;
    }

BLOCKED: 表示线程执行在进入(或重新进入)同步代码块或者同步方法前阻塞等待监视器锁(monitor lock),通常是在调用了Object.wait方法后进入BLOCKED状态;

WAITING:处于waiting状态线程通常是调用了以下方法:

1. Object.wait(with no timeout)
2. Thread.join(with no timeout)
3. LockSupport.park

比如一个调用了某个对象的 Object.wait 方法的线程会等待另一个线程调用此对象的Object.notify() 或 Object.notifyAll()。一个调用了 Thread.join 方法的线程会等待指定的线程结束

TIMED_WAITING: 一个正在限时等待另一个线程执行一个动作的线程处于这一状态,通常调用以下方法

Thread.sleep
带时限(timeout)的 Object.wait
带时限(timeout)的 Thread.join
LockSupport.parkNanos
LockSupport.parkUntil

经典的生产者-消费者模型解析以上线程状态:

public class Consumer implements Runnable{
    private final Cola cola;

    Consumer(Cola cola){
        this.cola =cola;
    }

    @Override
    public void run(){
        synchronized (cola){
            while(cola.colaCount < 1){
                try {
                    cola.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            cola.colaCount --;
            cola.drink();
            cola.notify();
        }
    }
}


public class Producer implements Runnable{
    private final Cola cola;

    Producer(Cola cola){
        this.cola = cola;
    }

    @Override
    public void run(){
        synchronized (this.cola){
            cola.colaCount ++;
            System.out.println("Cola be producing...current cola qty is " + cola.colaCount);
            cola.notifyAll();
        }
    }
}


// 测试

public static void main(String[] args) throws InterruptedException {
        // monitor lock
        Cola cola = new Cola();

        Thread consumerA = new Thread(new Consumer(cola));
        consumerA.setName("consumerA");
        Thread consumerB = new Thread(new Consumer(cola));
        consumerB.setName("consumerB");

        Thread producer = new Thread(new Producer(cola));

        // 启动消费者线程
        consumerA.start();
        consumerB.start();

        Thread.sleep(100);

        System.out.println("ConsumerA state is: " + consumerA.getState());
        System.out.println("ConsumerB state is: " + consumerB.getState());

        // 启动生产者线程
        producer.start();

        Thread.sleep(100);

        System.out.println("ConsumerA state is: " + consumerA.getState());
        System.out.println("ConsumerB state is: " + consumerB.getState());
    }

运行结果:

ConsumerA state is: WAITING
ConsumerB state is: WAITING
Cola be producing...current cola qty is 1
consumerB be consuming...current cola qty is 0
ConsumerA state is: BLOCKED
ConsumerB state is: TIMED_WAITING

测试代码看出,消费者A,B在分别进入同步方法时由于条件不满足调用了Object.wait方法后线程状态为waiting,此时它们都已释放处于对象Cola监视器锁并处于等待队列(wait set)中。生产者线程增加colaCount后调用notify All方法唤醒wait set中的消费者线程,此时它们将从wait set进入entry set竞争重新进入同步方法,线程状态由waiting -> blocked, 等待调度器调度。同一时刻只有一个消费者线程能到活得资源对象monitor lock,可以看到consumerB拿到了锁并执行sleep方法,线程状态-> time_waiting, consumerA因为没能获取到锁仍然处于blocked状态。

操作系统层面线程状态与JVM线程状态的关系,可以看出JVM线程状态中blocked,waiting和time_waiting本质上是OS中wait状态的细分

image.png

线程安全

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程讲如何交替执行,并且在主线程中不需要做额外的同步或协调,这个类都能表现出正确的行为,那么就程这个类是线程安全的。

线程安全实现策略

1. 线程封闭:线程封闭的对象只能有一个线程拥有,对象被封闭在线程中,并且只能由这个 线程修改 
2.只读共享:在没有额外的同步的情况下,共享的只读对象可以由多个线程并发访问,但任何线程都不能修改他。共享的只读对象包括不可变对象和事实不可变对象。 
3.线程安全共享:线程安全的对象在其内部实现同步,因此多个线程可以通过对象的共有接口 进行访问而不需要进一步的同步 
4.保护对象:被保护的对象只能通过持有特定的锁来访问。保护对象包括封装在其他的线程安全的对象中的对象,以及已发布的并且由某个特定锁保护的对象。

线程中断

  1. interrupt()只是改变中断状态而已. interrupt()不会中断一个正在运行的线程。这一方法实际上完成的是,给受

阻塞的线程抛出一个中断信号,这样受阻线程就得以退出阻塞的状态。更确切 的说,如果线程被Object.wait, 

Thread.join和Thread.sleep三种方法之一阻塞,    那么,它将接收到一个中断异常(InterruptedException),

从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到InterruptedException

异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态

2.interrupted()方法会检查当前线程的中断状态,如果为 "被中断状态 "则改变当前线程为 "非中断状态 "并返回

true,如果为 "非中断状态 "则返回false,它不仅检查当前线程是否为中断状态,而且在保证当前线程状态恢复为非中

断状态,所以它叫 "interrupted ",是说中断的状态已经结束(到非中断状态了) 

3.isInterrupted()方法则仅仅检查线程对象对应的线程是否是中断状态,并不改变它的状态.

interrupt 中断操作时,非自身打断需要先检测是否有中断权限,这由jvm的安全机制配置;

如果线程处于sleep, wait, join 等状态,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常;

如果线程处于I/O阻塞状态,将会抛出ClosedByInterruptException(IOException的子类)异常;

如果线程在Selector上被阻塞,select方法将立即返回;

如果非以上情况,将直接标记 interrupt 状态;

注意:interrupt 操作不会打断所有阻塞,只有上述阻塞情况才在jvm的打断范围内,如处于锁阻塞的线程,不会受 interrupt 中断;

线程是什么

父线程如何捕获子线程异常

异常是什么

Spring中线程与事务关系

线程池任务处理完成而线程未结束时事务是否会提交


粥于于
9 声望1 粉丝

代码搬运工


下一篇 »
JDK线程池