我们知道,线程有五种生命周期:新建,就绪,运行,阻塞,死亡.
在我们编写,运行代码的过程中,可能出现线程死锁,线程阻塞等问题,下面介绍线程产生这些问题的原因,及解决的方案,保证线程能正常运行.
一、线程阻塞
线程阻塞的原因
1.调用sleep(),让线程变为睡眠状态,但是手里还拿着这个资源不放,后面的线程一直等待它醒来,然后等待它执行完,才能轮到下一个线程
2.调用join()方法,相当于插队,等插入的新线程执行完,再执行回原来的线程
3.调用wait()方法,进入等待状态,把CPU交出去,然后"睡去",但是该线程不会自然醒,需要用notify()方法唤醒
4.调用yield()方法,释放资源,然后与其他线程一起抢该资源
二、线程的终止
在java中,我们不采用stop()方法来终止线程,若是使用stop方法终止线程,会使得输出的信息破坏或者不完整,而且破坏了原子性的操作.
如何让线程终止,下面介绍两种常用的方法
1.通过给定属性,然后通过控制属性值来破坏继续执行的条件
private boolean flag = true;//属性 //线程实现Runnable接口,重写run方法 @Override public void run() { int i = 0; //通过flag来控制循环 while (flag) { System.out.println("执行线程_" + i); i++; } }
//在实现方法中,若要想结束, flag = false;
2.使用interrupt()方法在线程阻塞状态下结束线程。
public class MyRun implements Runnable { @Override public void run() { int i = 0; while(true){ System.out.println("线程_"+i); i++; try { Thread.sleep(500); } catch (InterruptedException e) { break; } } } }
public class Test { public static void main(String[] args) { MyRun mr = new MyRun(); Thread th = new Thread(mr); th.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //使用interrupt()方法结束该线程 th.interrupt(); } }
三、线程同步
在java中,通过关键字synchronized给对象加互斥锁,达到多个线程共享数据的操作,叫做线程同步.
1.同步代码块
synchronized放在对象前面限制一段代码的执行
Object obj = new Object(); ... synchronized(this){//this被加锁,任何其他要锁this的方法和代码块被阻塞. 需要同步的代码; } ... synchronized(obj){//obj被加锁,任何其他要锁obj的代码块被阻塞. 需要同步的代码; }
2.同步方法
同步非静态方法:synchronized放在方法声明中,表示整个方法为同步方法,锁定this对象,如果有一个线程进入了该方法,其他线程要想使用当前this对象的任何同步方法,都必须等待前一个线程执行完该同步方法之后
public synchronized void method(){ … }
同步static方法: synchronized放在static方法声明中,表示锁定该类的class对象,如果有一个线程进入了该方法,其他线程要想使用当前类中的任何同步静态方法,都必须等待前一个线程执行完该同步方法之后;其他非同步方法及非静态的同步方法的执行不受影响
public synchronized static void method(){ … }
四、线程死锁
1.产生死锁的原因
1> 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用 2> 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。 3> 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占用。 4> 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路
2.解决方法
在我们的项目中,为了避免死锁,以上四个条件,破坏其中任意一个,死锁就结束了
下面通过破坏请求和保持条件,通过获得-释放资源的方式,让每个线程都能拥有过资源
//解决死锁 public class MyRun implements Runnable{ Object a = new Object(); Object b = new Object(); @Override public void run() { synchronized (a){ System.out.println(Thread.currentThread().getName()+"获得了a资源"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"释放了a资源"); } synchronized (b){ System.out.println(Thread.currentThread().getName()+"获得了b资源"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"释放了b资源"); } } public static void main(String[] args) { MyRun myRun = new MyRun(); Thread t1 = new Thread(myRun,"线程1"); Thread t2 = new Thread(myRun,"线程2"); t1.start(); t2.start(); } }
五、总结
1.过多的同步会导致死锁
2.synchronized修饰静态方法,如果调用该方法,将会锁住整个类
3.synchronized不能被抽象类里的抽象方法,和接口中的方法使用,synchronized修饰的方法,可以是静态,也可以是非静态
4.当任意一个线程进入到一个对象中的任意一个同步方法时,这个对象的所有同步方法都被锁定了
5.两个被synchronized修饰过的方法,静态同步方法一起执行:锁的是类.class ,不需要等待;非静态同步方法:锁的是this,需要等待上一个释放
标签:同步,java,Thread,synchronized,线程,多线程,方法,public From: https://www.cnblogs.com/pilpill/p/16633649.html