Java并发编程的艺术,学习笔记(一)线程

线程

使用多线程的好处

  1. 可以更好的利用cpu资源
  2. 能有更快的响应时间(使用多线程合理的情况下)
  3. 更好的编程模型

线程的状态

在这里插入图片描述
如上图详细的描述了线程的生命周期,不同事件触发,而导致的状态转换。

启动和终止线程

线程的启动

thread.start();
线程启动的代码形式,即 当前线程同步告知jvm,如果线程规划器空间的话,则会启动线程。

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

源码中可以看出这是一个同步方法,且启动的时候,检查threadStatus,并将自己添加进group中去,然后调用start0() 本地方法。

线程的中断

thread.interrupt(); 实际就是设置中断的标识。
如果在阻塞的状态下,例如,sleep()、wait、join,则会抛出InterruptedException。
实际的用途(根据状态标志判断是否退出线程)还是看一段代码方便:

public void run() {  
            try {  
                while (true){  
                    Thread.sleep(1000l);//阻塞状态,线程被调用了interrupte()方法,清除中断标志,抛出InterruptedException  
                    //dosomething  
                    boolean isIn = this.isInterrupted();  
                    //运行状态,线程被调用了interrupte()方法,中断标志被设置为true  
                    //非阻塞状态中进行中断线程操作  
                    if(isIn) break;//退出循环,中断进程  
                }  
            }catch (InterruptedException e){//阻塞状态中进行中断线程操作  
                boolean isIn = this.isInterrupted();//退出阻塞状态,且中断标志被清除,重新设置为false,所以此处的isIn为false  
                return;//退出run方法,中断进程  
            }  
        }  

抛出异常后,线程的中断标志会重新设置为false。

线程间的通讯

线程之间的通讯其实很简单直观,对共享变量的多线程修改就是一种通讯方式,但是为了保证这种方式不出问题,就需要进行额外的同步,其代价也是相当的大的。

使用volatile和synchronized关键字

volatile 保证了可见性,在jvm 的时候已经学习过了可见性。
synchronized关键字则是对方法或者代码块进行同步,里面应该是包括对变量的访问。

使用等待/通知机制

我们可以看到Java的大父类,Object,里面有许多默认的方法其中就包括了wait() 和 notify() notifyAll() 方法,可以看出Java当初设计的时候就是考虑这多线程方面去的。
假设:线程A 调用了对象T的wait方法,就可以理解为 线程A需要一个T对象 notify或者notifyAll的方法才能进行下面的步骤。现有个线程B调用了T的notify方法,线程A则继续开始运行。

等待/通知有个经典公式

等待:
1)获取对象的锁
2)如果条件不满足,那么调用对象的wait方法,被通知后再检查条件
3)条件满足则执行对应的逻辑
通知:
1)获取对象的锁
2)改变条件
3)通知所有等待在对象上的线程。

public class Service {
	static boolean flag = true;
    public void testMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin wait() ThreadName="
                        + Thread.currentThread().getName());
                // 循环判断,如果flag 为true则会进行等待队列
                // 进入等待队列后,线程则会释放锁,而不会占用着资源      
                while(flag){
                	lock.wait();
                }
                System.out.println("  end wait() ThreadName="
                        + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void synNotifyMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin notify() ThreadName="
                        + Thread.currentThread().getName() + " time="
                        + System.currentTimeMillis());
                flag = false;
                lock.notify();
                Thread.sleep(5000);
                System.out.println("  end notify() ThreadName="
                        + Thread.currentThread().getName() + " time="
                        + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

thread.join()

先看下join的源码,可以简化成下面的这个样子

    public final synchronized void join()
    throws InterruptedException {
         while (isAlive()) {
                wait(0);
            }
    }

他调用的就是线程对象的wait方法。
假设我们在threadA上调用threadB的join方法。那threadA则会等待threadB线程结束才会从join()返回。

ThreadLocal

TreadLocal,本地线程变量,可见是单线程里面的变量,所以可以认为是线程安全的。
在这里插入图片描述
上面这图看气力好像有点复杂,我们还是结合图对着源码来分析。

public class ThreadLocal<T> {
	
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }    
}

以上是对ThreadLocal 源码的部分截取。

  1. 首先要明白的是,Thread类中有个包可见访问字段 threadLocals,他是一个map, key值为threadLocal对象,value则是用户设置的对象(A、B、C。。。)
  2. Thread 里面的 treadLocals对我们是透明的,所以我们在使用的时候,只需要创建ThreadLocal的对象就好了。
    就像这样:
ThreadLocal<Integer> threadLocal1 = new ThreadLocal<>();
ThreadLocal<String> threadLocal2 = new ThreadLocal<>();

在多线程的情况下,多条线程执行这两段代码的的时候,如果有设置值的话。
那他们的线程对象 thread 里面的threadLocals 就都存在这两个对象。
所以他们设置的时候会先去 线程中获取 线程里面的map。

 Thread t = Thread.currentThread();
 ThreadLocalMap map = getMap(t);

更通俗点就是:我有100个线程,每个线程都保存引用为“java.lang.ThreadLocal@610455d6”的threadLocal对象,但是我100个线程中保存的值都是不相同的。 并且我这100个线程里面有的是ThreadLocalMap 可以保存各种各样的 ThreadLocal对象,以及对应的值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值