多线程二
1. 线程的常用方法
Thread.sleep(毫秒数),让线程休眠,时间到,自动唤醒并继续执行
Thread.currentThread():表示获取当前正在执行的线程对象
public finalboolean isAlive():测试线程是否处于活动状态
getName():获取线程名字
优先级:可以给线程设置优先级,范围是1-10,10优先级最高
setPriority(数字):给线程设置优先级,这里优先级高,并不代表一定先执行,只是增加了抢占到资源的机会
getPriority():获取线程的优先级,
Thread提供了三个优先级常量:
Thread.MAX_PRIORITY=10
NORM_PRIORITY = 5
MIN_PRIORITY = 1
Main方法的优先级,是5
yield():当前线程暂停执行,与其他线程同时抢占资源,如果还是自己抢占到,则继续执行后续的代码,如果是其他线程抢占到,则其他线程先执行.
join():当前线程暂停执行,新加入的线程开始执行,当新线程执行完之后,再执行当前线程
yield()方法使用如下,
先准备一个A线程,在执行A线程过程中准备B线程,并让B线程就绪,然后A线程执行到一半之后,A线程礼让,此时A和B都就绪,然后各自抢占资源,谁抢到资源谁执行,如下:
A线程代码如下:
package cn.sz.gl.test02; public class MyRunA implements Runnable { @Override public void run() { MyRunB b = new MyRunB(); Thread tb = new Thread(b,"B"); tb.start(); System.out.println("线程A启动.."); Thread.currentThread().yield(); System.out.println("线程A结束.."); } }B线程如下:
package cn.sz.gl.test02; public class MyRunB implements Runnable { @Override public void run() { System.out.println("线程B启动.."); System.out.println("线程B结束..."); } }测试方法如下:
package cn.sz.gl.test02; public class Test { public static void main(String[] args) { MyRunA a = new MyRunA(); Thread ta = new Thread(a,"A"); ta.start(); } }join()方法使用如下:
先准备一个A线程,在执行A线程过程中准备B线程,并让B线程就绪,然后A线程执行到一半之后,让B线程调用join方法,此时A线程阻塞,B 线程启动执行,当B线程执行完毕后,A线程再继续执行
A线程代码如下:
package cn.sz.gl.test02; public class MyRunA implements Runnable { @Override public void run() { MyRunB b = new MyRunB(); Thread tb = new Thread(b); tb.start(); System.out.println("线程A启动.."); try { tb.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程A结束.."); } }B 线程代码如下:
package cn.sz.gl.test02; public class MyRunB implements Runnable { @Override public void run() { System.out.println("线程B启动.."); System.out.println("线程B结束..."); } }测试代码如下:
package cn.sz.gl.test02; public class Test { public static void main(String[] args) { MyRunA a = new MyRunA(); Thread ta = new Thread(a,"A"); ta.start(); } }
2. 线程的终止
第一种,通过给定属性,然后通过控制属性值来破坏继续执行的条件
线程代码如下:
package cn.sz.gl.test03; public class MyRun implements Runnable { private boolean flag = true; @Override public void run() { int i = 0; while (flag) { System.out.println("执行线程_" + i); i++; } } public void changeFlag() { flag = false; } }测试代码如下:
package cn.sz.gl.test03; public class Test { public static void main(String[] args) { MyRun mr = new MyRun(); new Thread(mr).start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } mr.changeFlag(); } }第二种,对于正在执行的线程,interrupt()是给线程设置中断标志;interrupted()是检测中断并清除中断状态;isInterrupted()只检测中断。还有重要的⼀点就是interrupted()作⽤于当前线程,interrupt()和isInterrupted()作⽤于此线程,即代码中调⽤此⽅法的线程
线程代码如下:
package cn.sz.gl.test03; public class MyRun2 implements Runnable { @Override public void run() { int i = 0; while(!Thread.interrupted()){ System.out.println("我的线程_"+i); i++; } } }测试代码如下:
package cn.sz.gl.test03; public class Test2 { public static void main(String[] args) { //System.out.println(Thread.interrupted());//默认为false MyRun2 mr = new MyRun2(); Thread th = new Thread(mr); th.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } th.interrupt(); } }第三种,如果线程因为执行join(),sleep()或是wait()而进入了阻塞状态,此时要想停止它,可以让它调用interrupt(),程序会抛出InterruptedException异常而被catch()子句捕获,进行处理
线程代码如下:
package cn.sz.gl.test03; public class MyRun3 implements Runnable { @Override public void run() { int i = 0; while(true){ System.out.println("线程_"+i); i++; try { Thread.sleep(500); } catch (InterruptedException e) { break; } } } }测试代码如下:
package cn.sz.gl.test03; public class Test3 { public static void main(String[] args) { MyRun3 mr = new MyRun3(); Thread th = new Thread(mr); th.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //当调用interrupt方法时,可以改变interrupted的值 //当th线程中有处理InterruptedException异常的时候,如果该线程调用了interrupt方法,那么该线程会抛出InterruptedException异常 th.interrupt(); } }
3. 线程的同步
在Java语言中,引入对象互斥锁的概念,保证共享数据操作的完整性;
每个对象都对应于一个可称为"互斥锁"的标记,这个标记保证在任一时刻,只能有一个线程访问对象;
用关键字synchronized给对象加互斥锁.
3.1 同步代码块
synchronized放在对象前面限制一段代码的执行,其语法如下:
Object obj = new Object(); ... synchronized(this){//this被加锁,任何其他要锁this的方法和代码块被阻塞. 需要同步的代码; } ... synchronized(obj){//obj被加锁,任何其他要锁obj的代码块被阻塞. 需要同步的代码; }
3.2 同步方法
同步非静态方法:synchronized放在方法声明中,表示整个方法为同步方法,锁定this对象,如果有一个线程进入了该方法,其他线程要想使用当前this对象的任何同步方法,都必须等待前一个线程执行完该同步方法之后
如下:
public synchronized void method1(){ … } public synchronized void method2(){ … } public synchronized void method3(){ … }同步static方法: synchronized放在static方法声明中,表示锁定该类的class对象(xxx.class,是Class类型的,是描述一个类的信息的对象)
如果有一个线程进入了该方法,其他线程要想使用当前类中的任何同步静态方法,都必须等待前一个线程执行完该同步方法之后;其他非同步方法及非静态的同步方法的执行不受影响
如下:
public synchronized static void method1(){ … } public synchronized static void method2(){ … }注意:
当前线程调用本同步方法时,其他线程是不允许调用本对象中其他同步方法
当前线程在调用本同步静态方法时,其他线程是不能够调用本类中的其他同步静态方法的,但是可以调用本类中的其他非同步方法或者非静态同步方法
对于同步静态方法,此时锁定的是对应的类型(类名.class),所有属于该类型的对象,都在其锁定范围内,此时可以实现方法的同步调用
4. 线程阻塞
同步阻塞
调用sleep(),join()方法的时候,sleep()转为休眠,时间到自动唤醒
wait(),notify()方法配套,转为等待,同时释放资源,通过notify()方法唤醒
yield(),释放资源,与其他线程重新抢占资源,抢到就执行
suspend()和resume(),由jdk1.0提供,jdk1.2之后就被放弃了,它也是让线程暂停,但是它不释放资源,导致死锁出现
5. 线程死锁
死锁产生的必要条件:
1> 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2> 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3> 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占用。
4> 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路
在我们的项目中,我们不希望看到死锁的发生,为了避免,以上四个条件,破坏其中任意一个,死锁就结束了
注意:过多的同步会导致死锁
死锁示例如下:
package cn.sz.gl.no5; public class MyRun implements Runnable { private static Object a = new Object(); private static Object b = new Object(); private boolean flag = true; public void changeFlag(){ flag = false; } @Override public void run() { if(flag){ synchronized (a) { System.out.println(Thread.currentThread().getName()+"锁定了资源a"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (b) { System.out.println(Thread.currentThread().getName()+"锁定资源b"); } } }else{ synchronized (b) { System.out.println(Thread.currentThread().getName()+"锁定了资源b"); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (a) { System.out.println(Thread.currentThread().getName()+"锁定资源a"); } } } } }测试如下:
package cn.sz.gl.no5; public class Test { public static void main(String[] args) { MyRun mra = new MyRun(); new Thread(mra,"线程1").start(); MyRun mrb = new MyRun(); mrb.changeFlag(); new Thread(mrb,"线程2").start(); } }