多线程基础学习二
Thread类
位置:位于java.lang包中
1.设置线程的优先级:
setPriority(int i);参数为一个整数,有三个值:
Thread.MAX_PRIORITY 最高优先级 10
Thread.MIN_PRIORITY 最低优先级 1
Thread.MORM_PRIORITY 一般优先级 5
注意:依据平台的不同,OS对优先级的处理方式不一样,而且划分等级也可能不一样,所以这种方式不能保证线程的执行顺序,不鼓励使用。
2.线程的状态发生和控制
1.yield()方法,只要一个线程调用了.yield()方法,线程就立即放弃CPU的时间片,从运行状态直接进入可运行状态。
2.阻塞态
三种方法可以导致线程变成组赛态:
A.等待数据输入,如I/O中一些read方法
B.调用了Sleep()方法。休眠指定时间
C.对另一个线程调用了join方法。如t2.join()。则表示当前线程要等到t2线程结束后,才会打破阻塞状态。
相应的从阻塞状态到可运行状态:
A.数据输入结束。
B.Sleep()方法所有休眠时间到达
C.知道t2线程结束
注意:Sleep()方法会抛出InterruptedException,若果在run()的方法里面调用了则不能调用了Sleep,则不会抛出InterruptedException异常,
因为父类和runnable接口都没有抛出异常,根据子方法绝不能比父方法抛出更多的原则,所以必须用try .. catch处理。
join()方法可以说把两个线程合并成一个线程,它也会抛出一个InterruptedException的异常。
3.线程同步
当多个线程并发的访问同一个共享资源时,就有可能导致共享资源的原子操作分开执行,这样的话就会导致共享资源的数据不一样和不完整的情况,这种共享资源称之为临界资源
解决问题的办法
同步锁机制:
1. 一个对象除了属性和方法,还有互斥锁,锁池,和等待队列。
2.每个对象都有一把锁,如果把这边锁分配给一个线程,则在此线程使用期间,其他线程就不能获得这个锁资源了,直到这个线程释放了锁资源。
3.锁,锁池,和等待队列是对象自有的,用来分配给线程使用的,简单的变量没有
实现同步的关键字:synchronized
1.可以用来修饰代码块和方法
如:1.修饰方法,就是对当前对象加锁,也叫做内同步法
synchronized void m(){}
总结:凡是进入执行此代码块的方法,都必须拿到此对象的锁,否则的进入阻塞状态(对象的锁池),拿到锁的线程执行完后会释放锁资源(OS分配)。
2.修饰代码块,对‘对象’加锁,又叫做外同步法
如:
void m(){
synchronized (对象名) {}//这里也是对当前对象加锁和内同步法是一样的
}
总结:外同步法比内同步法更加的灵活,可以对不同的对象加锁,内同步法只能对当前对象加锁。
静态方法允许使用synchronized,但构造方法是不允许的,因为没有对象。
由此我们总结在:Verctor和ArrayList两个集合
Verctor是线程安全的,因为Verctor的每个方法都加了synchronized修饰的,所以是线程安全的,但性能不高。
ArrayList是线程不安全,因为方法没有用synchronized修饰,为了线程安全,可以使用外同步法,如:
synchrnized (obj){
obj.add(aa);
}//同样可以实现代码块的线程安全
注意:不使用线程安全的原因:加锁是需要消耗资源,cpu时间,自然的效率就降低了,所以对该加锁的需要加,不该加锁的不要加锁。
线程状态图:
注意:
1.notify()和notifyAll()方法纯粹是通知其他在等待队列中的线程,只是把它们唤醒,状态到池锁,它不会让线程放弃锁资源。
2.拥有此对象的锁资源,进入其他对象的锁池是不会放弃自己拥有的对象锁资源,这也是造成死锁的原因。
3.锁池状态:
也是一种特殊的阻塞状态
当拥有此对象的线程进入已经加锁的临界资源,又没有此对象的锁时,则线程就进入此对象的锁池,等待os调配,分配锁标记。
4.等待队列
也是一种特殊的阻塞
当一个线程调用了对象的wait()方法,那么线程就会释放它拥有的所有的锁标记,并自动进入对象的等待队列,直到有另外一个线程调用对象的notify()和notifyAll(),则此线程才会从等待队列进入锁池,等待os分配锁标记。