多线程知识梳理2
---------------------------------------------------------------------------------------------------------------------------------------------
1、实际开发 匿名内部类使用很广泛。
2、使用 Lock 替代 synchronized 显示加锁解锁 >> lock()、unlock()
3、利用 可以有多个 Condition 对象 进行指定signle 和 await
4、Condition中 await signal signalAll 替换原来Object中等待唤醒方法。
5、使用LOCK关键代码:
2、使用 Lock 替代 synchronized 显示加锁解锁 >> lock()、unlock()
3、利用 可以有多个 Condition 对象 进行指定signle 和 await
4、Condition中 await signal signalAll 替换原来Object中等待唤醒方法。
5、使用LOCK关键代码:
private Lock lock = new ReentrantLock();private Condition conAA = lock.newCondition();private Condition conBB = lock.newCondition();
6、清除冻结状态:interrupt() 使用时,一定要配对更改 标签。
7、守护线程、后台线程setDaemon(boolean) 依附于主线程,
8、临时加入线程 join() 能够获取执行权,被剥夺执行权的线程只有在该线程结束后才会参加抢夺
7、守护线程、后台线程setDaemon(boolean) 依附于主线程,
8、临时加入线程 join() 能够获取执行权,被剥夺执行权的线程只有在该线程结束后才会参加抢夺
执行join() 方法前的线程会和当前线程抢夺资源
9、线程组、优先级 setPriority设定优先级 记住MAX、MIN、MOR、NORM
10、暂停 线程 yield() 临时的释放执行权,减缓线程的运行频率,保证每个线程能较为平均的运行
-------
11、同步代码的锁,最好的是该类中 最唯一的对象 r 最具唯一性
12、共享数据 都必须同步 synchronized(r) 2处都是共享数据
13、通过一个 flag标记,来判断,一个线程是否结束, 通过 wait 等待 和 notify 唤醒机制,
14、wait() notify() notifyAll 必须同一把锁 ,锁可以是任意的对象,而被任意对象调用的方法定义在Object中
15、notify() 适合仅仅2个线程的时候。当大于2个线程的时候,由于唤醒机制,容易出现安全问题。
16、当同时有多个线程,执行相同功能时候,必须写 while 循环判断标记 使用 notifyAll 唤醒机制
-------------------------------------------------------
10、暂停 线程 yield() 临时的释放执行权,减缓线程的运行频率,保证每个线程能较为平均的运行
-------
11、同步代码的锁,最好的是该类中 最唯一的对象 r 最具唯一性
12、共享数据 都必须同步 synchronized(r) 2处都是共享数据
13、通过一个 flag标记,来判断,一个线程是否结束, 通过 wait 等待 和 notify 唤醒机制,
14、wait() notify() notifyAll 必须同一把锁 ,锁可以是任意的对象,而被任意对象调用的方法定义在Object中
15、notify() 适合仅仅2个线程的时候。当大于2个线程的时候,由于唤醒机制,容易出现安全问题。
16、当同时有多个线程,执行相同功能时候,必须写 while 循环判断标记 使用 notifyAll 唤醒机制
-------------------------------------------------------
【1】
多线程中存在的安全问题
//例子1--2个线程。
多线程中存在的安全问题
//例子1--2个线程。
class names2
{
public static void main(String[] args) {
Res r = new Res();
new Thread(new input(r)).start();
new Thread(new output(r)).start();
}
}
class Res{
private String name;
private String sex;
boolean flag;
public synchronized void set(String name,String sex){
if(flag)
try{this.wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
notify();
}
public synchronized void out(){
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(name+">>"+sex);
flag = false;
notify();
}
}
class input implements Runnable
{
private Res r;
input(Res r){
this.r = r;
}
public void run(){
int x = 0 ;
while(true){
if(x == 0)
r.set("lili","11111111");
else
r.set("达到","-----------");
x = (x+1)%2;
}
}
}
class output implements Runnable{
private Res r;
private boolean flag;
output(Res r){
this.r = r;
}
public void run(){
while(true){
r.out();
}
}
}
生产和消费 4个线程 使用上诉逻辑 显然是不行的。
class shangping {
public static void main(String[] args) {
Resource r = new Resource();
new Thread(new Producer(r)).start();
new Thread(new Producer(r)).start();
new Thread(new Consumer(r)).start();
new Thread(new Consumer(r)).start();
}
}
class Resource{
private String name;
private int num=1;
private boolean flag;
public synchronized void set(String name){
while(flag)// 循环致使每一次都会判断
try{this.wait();}catch(Exception e){}
this.name = name+".."+num++;
System.out.println(Thread.currentThread().getName()+">>>"+this.name);
this.flag = true;
this.notifyAll();//唤醒全部线程,避免只唤醒本方线程
}
public synchronized void out(){
while(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"================="+this.name);
this.flag = false;
this.notifyAll();
}
}
//sheng chan
class Producer implements Runnable{
private Resource r ;
Producer(Resource r ){
this.r = r ;
}
public void run(){
while(true){
r.set("CPU");
}
}
}
class Consumer implements Runnable{
private Resource r ;
Consumer(Resource r ){
this.r = r ;
}
public void run(){
while(true){
r.out();
}
}
}
例子2是对例子1进行了线程的扩编, 发现 notify局限性,1、只会判断标记一次,如果改为while
又会出现全部卧倒的可能性。
可以看出,多线程的安全问题是一个需求十分严谨却又痛苦万分的。然后来看看更新优化
【2】
【2-1】1.5新特性:
将同步 synchronized 替换成 显示 的 Lock
将Objcet 中替换成Condition 对象 wait notify notifyAll >>>>await signal signalAll
似乎没有太大的更新,
但是 Lock 中可以有 多个 Condition 对象
也就是说,在读取标签的时候,可以指定让持有具体锁的那个对象 await 或者 signal
这样一样,代码就明朗了很多,不会出现只唤醒一方的情况。有效的避免了 全部卧倒。
替换后的代码。其实思路是一样了,不同的就是把加锁,释放锁的过程放在了名面上。
class Resource{
private String name;
private int num=1;
private boolean flag;
private Lock lock = new ReentrantLock();
private Condition conAA = lock.newCondition();
private Condition conBB = lock.newCondition();
public void set(String name){
lock.lock();
try{
while(flag)
try{conAA.await();}catch(InterruptedException e){}//true>>让A方都await
this.name = name+".."+num++;
System.out.println(Thread.currentThread().getName()+">>>"+this.name);
flag = true;
conBB.signalAll();// A方完事了>>>flag = true>>让B方 醒
}
finally{
lock.unlock();
}
}
public void out(){
lock.lock();
try {
while(!flag)
try{conBB.await();}catch(InterruptedException e){}// false>> 让B都await
System.out.println(Thread.currentThread().getName()+"================="+this.name);
flag = false;
conAA.signalAll();// B 方完事了>>>flag = false>> 让A 方醒
}
finally{
lock.unlock();
}
}
}
InterruptedException : 当一个线程在运行中,被中断的异常。
多线程的运行,通常都是循环结构,控制住 循环,就可以让run方法结束,也就结束了线程。
特殊情况:
当线程处于冻结状态,就不会读取标记,那么线程就不会结束
当没有指定的方式让冻结的线程恢复到运行状态,这时需要对冻结状态进行清除
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
【2-2】
Thread 提供方法: interrupt() 清除冻结状态
使用时,1,interrupt清除,2改变标签值。
class stop implements Runnable{
private boolean flag = true;
public synchronized void run(){
while(flag){
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"!!!!!!!!!!!!");
flag = false;//【2】清除冻结状态后 改变标签属性 达到结束目的。
}
System.out.println(Thread.currentThread().getName()+">>>>RUN");
}
}
public void changeFlag(){
flag = false;
}
}
public class interrupt {
public static void main(String[] args) {
stop s = new stop();
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
t1.start();
t2.start();
int num = 0 ;
while(true){
if(num++ ==60){
//s.changeFlag();
t1.interrupt();//【1】 清除 冻结状态
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"MAIN------------");
}
System.out.println("over");
}
}
【2-3】
void setDaemon(boolean)
设置为守护线程或用户线程(后台线程)前台线程一结束,后台线程都结束
必须设定在 start 前。
上面例子部分修改一下
t1.setDaemon(true);//设定为守护线程
t2.setDaemon(true);
t1.start();
t2.start();
int num = 0 ;
while(true){
if(num++ ==60){
//t1.interrupt();//【1】 清除 冻结状态
break;
}
System.out.println(Thread.currentThread().getName()+"MAIN------------");
}
当没有 设定 守护线程 的时候,会出现等待现象,
而,当设定了后台后, 主线程 一结束,t1 t2 也跟着结束了。
注释掉 try 部分, 观看是否设定 后台线程 结果更为明显。
while(flag){
/* try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"!!!!!!!!!!!!");
flag = false;//【2】清除冻结状态后 改变标签属性 达到结束目的。
}
*/ System.out.println(Thread.currentThread().getName()+">>>>RUN");
}
【2-4】
join() 临时加入线程执行。
当进行多线程运算时,不论谁拥有执行权(假设B),当加入 A.join()
当读到A.join() 表示A线程要执行权,A 要优先执行。
A 线程结束后,才释放执行权,这个时候B 才会参与和他线程再相互抢夺执行权利
也就说说,A.join() 前只有A时, A执行
A.join() 前有A和C时, AC相互抢夺 执行权
最开始拿到执行权利的B 只能等待A 执行完毕后,才会参与权利抢夺。
public class jion {
public static void main(String[] args) {
jionD j = new jionD();
Thread t1 = new Thread(j);
Thread t2 = new Thread(j);
t1.start();
//t2.start();//当 t2在t1.join() 前时候,会和 t1 抢夺执行权。
try {t1.join();} catch (InterruptedException e) {}
t2.start();
for(int i = 0 ;i <70 ; i++){
System.out.println(Thread.currentThread().getName()+"-------"+i);
}
}
}
class jionD implements Runnable{
public void run() {
for(int i = 0 ;i< 70 ; i++){
System.out.println(Thread.currentThread().getName()+">>"+i);
}
}
}
总之一句话,join() 会剥夺拥有当前执行权的线程,一直到join() 对应的线程结束才会参与抢夺,至于 join() 对应的线程会不会被其他线程抢夺资源另说。
【2-5-1】
setPriority() 设定优先级
通过 Thread.currentThread().toString() 查看 到线程的优先级和线程组
ThreadGroup 线程组对象 据说用的很少
创建线程组,把线程装入该对象,即是一个线程组
优先级 :1-10级 默认优先级 5
MAX_PRIORITY >>10
MIN_PRIORITY >>5
NORM_PRIORITY >>1
根据上面的例子
t2.setPriority(MAX_PRIORITY);
这个时候t2 线程的优先级最高,在运行越多时越明显,但不代表 ,其他线程抢夺不到资源。
【2-5-2】
yield () 方法
暂停当前正在执行的线程对象,并执行其他的线程。
class jionD implements Runnable{
public void run() {
for(int i = 0 ;i< 70 ; i++){
System.out.println(Thread.currentThread().getName()+">>"+i);
Thread.yield();//上列代码 修改部分
}
}
}
这个时候,t1 t2不论谁进来,会稍微减少线程运行频率,并保证每一个线程都有机会平均性的运行。
【3】
老师给出的,实际开发中 常用的 线程方式, 感觉 匿名内部类的使用是非常普遍的。
public class ThreadTest {
public static void main(String[] args) {
//
new Thread()
{
public void run(){
for(int i = 0 ;i < 100 ; i++ ){
System.out.println("匿名内部类线程");
}
}
}.start();
for(int i = 0 ;i < 100 ; i++ ){
System.out.println("主线程部分");
}
//
Runnable r = new Runnable()
{
public void run(){
for(int i = 0 ;i < 100 ; i++ ){
System.out.println("不同封装方式达到同时运行的效果");
}
}
};
new Thread(r).start();
}
}
//end
---------------------------------------------------------------------------------------------------------------------------------------------
----------
android培训、
java培训、期待与您交流!----------