一、线程
1.线程状态
新生、就绪、运行、睡眠、等待、阻塞、死亡
当创建类Thread或其子类的实例对象,新线程就产生,自动进入新生态
调用线程的成员方法start,线程就进入就绪态
就绪态的线程才能参与JVM对线程的调度,JVM按照一定的调度规则让一些就绪态的线程进入运行态
若存在多个处于就绪态的线程,则优先级高的线程优先进入运行态
已进入就绪状态但未获取到资源
多个线程共享资源,资源有限,JVM让有限个线程占有,让其它处于就绪态的线程因缺少资源自动进入阻塞台
处于运行态的线程调用sleep()方法后自动进入睡眠态,暂时停止运行
sleep()属于类静态方法,在哪个线程中出现,则哪个线程暂停运行
例如在B线程中执行A.sleep(1000),则是B线程暂停运行。一般用Thread.sleep(1000)
处于运行态的线程调用wait()方法进入等待态
当等待时或别的线程调用了notify()或notifyAll(),处于等待态的线程就可能重新回到就绪态,由JVM重新调度
执行完run()方法就进入死亡态
二、线程组
Thread t=Thread.currentThread();
ThreadGroup tg=t.getThreadGroup();
for(;tg!=null;tg=tg.getParent()){
int n=tg.activeCount();
System.out.println("线程组"+tg.getName()+" 包含 "+n+" 个线程");
Thread[] arr=new Thread[n];
int m=tg.enumerate(arr);
for(int i=0;i<m;i++){
System.out.println("\t"+arr[i].getName());
}
}
线程组main 包含 2 个线程
main
Monitor Ctrl-Break
线程组system 包含 6 个线程
Reference Handler
Finalizer
Signal Dispatcher
Attach Listener
main
Monitor Ctrl-Break
三、同步
Java虚拟机为每个对象配备了一把锁lock和一个等候集wait set
对象内部锁住的事一些 同步方法 和 同步语句块
一旦有线程进去运行对象的同步方法或同步语句块,对象锁就会自动锁上,其它需要进去的线程只能处于阻塞状态,等待锁的打开;线程执行完后对象锁会自动打开
若对象锁打开,多个线程等待,则优先级高的线程先进入,若优先级相同则随机
1.同步方式
对线程的同步处理的方式: 同步方法、同步语句块
实现2种方式的方法:对资源(内存)加锁
可能引发问题:死锁
死锁:程序的所有线程都处于 “阻塞态” 或 “等待态”
四、线程内存
JVM中,主内存和工作内存,每个线程对应一个工作内存,并共享主内存数据
1.对于普通变量
读操作优先读取工作内存的数据,若工作内存不存在,则从主内存复制一份到工作内存;
写操作只会修改工作内存中的副本数据,这种情况下,其它线程就无法读取变量的最新值
2.对于volatile变量
读操作时JVM会把工作内存中对应的值设置为无效,要求线程从主内存中读取数据;
写操作时JVM也会把工作内存中对应的数据刷到主内存中,这种情况下,其它线程可以读取变量的最新值
五、关键字
类对象锁、实例对象锁
1.synchronized
synchronized可以保证在同一时刻只有一个线程可执行某个方法或某个代码块,同时它还可以保证一个线程变化可见(可见性),即可代替volatile
2.volatile
volatile变量只保证每次读取时都从主存中读,每次修改时,都将修改后的值重新写入了主存
由于使用了volatile屏蔽了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要时才能使用该关键字
虽然volatile变量所有的写操作都能立刻被其它线程得知,但不代表volatile变量的运算在并发下时安全的,因为volatile只能保证内存可见性,却没有保证对变量操作的原子性。
比如volatile修饰的变量num=0;A线程对其执行10次num++,B线程也对其执行10次num++,但结果并不是num=20,因为num++不是一个原子操作。
(在访问volatile变量时不会执行加锁操作,因此不会使线程阻塞)
X++实际是读取-修改-写入的组合操作,必须以原子方式进行,而volatile不能提供必须的原子特性,实现正确的操作需要使X的值在操作期间保持不变
3.wait
线程在执行同步方法或同步语句块时调用Object的wait(),则该线程进入对象的等候集wait set,该线程变为等候态,同时对象锁自动打开
激活在等候集中的线程使用Object的notify()或notifyAll()
以上方法都只能在同步方法或同步语句块中调用
notify() 随机激活对象等候集中的其中1个线程
notifyAll() 激活对象等候集中的所有线程
被激活的线程离开等候集进入就绪态,由JVM调度
如果此时对象锁已打开则线程会重新进入原先运行的同步方法或同步语句块,而且执行的语句是原先wait时的下一条语句开始
4.join
等待调用join方法的线程结束,再继续执行
5.yield
让当前执行的线程让出cpu时间片(使当前线程重新回到可执行状态)