现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个Java程序,操作系统就会创建一个Java进程。现代操作系统调度的最小单元是线程,也叫轻量级进程(LightWeightProcess),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上快速切换,让使读者感觉到这些线程在同时执行。
更多的处理器核心
更快的响应
更好的编程模型
由于是操作系统给线程分配时间片的处理方式。那么便可以通过优先级设置处理的先后,确保处理器不会被独占。
启动:当前线程(即parent线程)同步告知Java虚拟机,只要线程规划器空闲,应立即启动调用start()方法的线程。
中断:即是线程的一个标识位属性,通过调用该线程的interrupt()方法对其进行中断操作。也可以调用静态方法Thread.interrupted()对当前线程的中断标识位进行复位,当抛出InterruptedException之前,Java虚拟机会先将该线程的中断标识位清除
,即此时调用isInterrupted()方法将会返回false。
通过设置一个boolean变量控制
public class Shutdown {
public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
// 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束
TimeUnit.SECONDS.sleep(1);
countThread.interrupt();
Runner two = new Runner();
countThread = new Thread(two, "CountThread");
countThread.start();
// 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为falseer结束
TimeUnit.SECONDS.sleep(1);
two.cancel();
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
while (on && !Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("Count i = " + i);
}
public void cancel() {
on = false;
}
}
}
volatile
通知线程间从共享内存
中获取,并刷新回各自的工作内存
,即是保证了对所有线程对变量访问的可见性。synchronized
可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性。
monitorenter
和monitorexit
指令。ACC_SYNCHRONIZED
来完成的。本质:对一个对象的监视器(monitor
)进行获取,而这个获取过程是排他的,也就是同一时刻只能有一个线程获取到由synchronized所保护对象的监视器。
任意线程对Object(Object由synchronized保护)的访问,首先要获得Object的监视器。如果获取失败,线程进入同步队列,线程状态变为BLOCKED。当访问Object的前驱(获得了锁的线程)释放了锁,则该释放操作唤醒阻塞在同步队列中的线程,使其重新尝试对监视器的获取。
等待/通知机制,是指一个线程A调用了对象O的
wait()
方法进入等待状态,而另一个线程B调用了对象O的notify()
或者notifyAll()
方法,线程A收到通知后从对象O的wait()
方法返回,进而执行后续操作。
notify
: 通知一个对象上等待的线程,使其从wait方法返回,而返回的前提是线程获取到了对象的锁。notifyAll
:通知所有等待在该对象上的线程。wait
: 调用该方法的线程进入WAITING
状态,只有等待另外线程的通知或被中断才会返回,需要注意,调用wait
方法后,会释放对象的锁。wait(long)
: 超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回。wait(long,int)
: 对于超时时间更细粒度的控制,可以达到纳秒。wait()
、notify()
和notifyAll()
时需要先对调用对象加锁。wait()
方法后,线程状态由RUNNING
变为WAITING
,并将当前线程放置到对象的等待队列。notify()
或notifyAll()
方法调用后,等待线程依旧不会从wait()
返回,需要调用notify()
或notifAll()
的线程释放锁之后,等待线程才有机会从wait()
返回。notify()
方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll()
方法则是将等待队列中所有的线程全部移到同步队列,被移动的线程状态由WAITING
变为BLOCKED
。wait()
方法返回的前提是获得了调用对象的锁。上图中,WaitThread首先获取了对象的锁,然后调用对象的
wait()
方法,从而放弃了锁并进入了对象的等待队列WaitQueue中,进入等待状态。由于WaitThread释放了对象的锁,NotifyThread随后获取了对象的锁,并调用对象的notify()
方法,将WaitThread从WaitQueue移到SynchronizedQueue中,此时WaitThread的状态变为阻塞状态。NotifyThread释放了锁之后,WaitThread再次获取到锁并从wait()
方法返回继续执行。
定义:如果一个线程A执行了thread.join()语句,当前线程A等待thread线程终止之后才从thread.join()返回。
ThreadLocal
,即线程变量,是一个以ThreadLocal
对象为键、任意对象为值的存储结构。ThreadLocal
为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal
的Map,可以存放若干个ThreadLocal。ThreadLocal
中。ThreadLocal
中。ThreadLocal
根本就没有竞争。内存泄露:
实际上ThreadLocalMap
中使用的key为ThreadLocal的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。所以如果ThreadLocal
没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来ThreadLocalMap
中使用这个ThreadLocal
的key也会被清理掉。但是,value是强引用,不会被清理,这样一来就会出现key为null的value。ThreadLocalMap
实现中已经考虑了这种情况,在调用set()、get()、remove()方法的时候,会清理掉key为null的记录。如果说会出现内存泄漏,那只有在出现了key为null的记录后,没有手动调用remove()方法,并且之后也不再调用get()、set()、remove()方法的情况下。
原文:https://www.cnblogs.com/lycsmzl/p/13213588.html