一、死锁
当同步中嵌套同步时,就有可能出现死锁现象。
代码示例:
- //写一个死锁程序
- //定义一个类来实现Runnable,并复写run方法
- class LockTest implements Runnable{
- private boolean flag;
- LockTest(boolean flag) {
- this.flag=flag;
- }
- public void run(){
- if(flag){
- while(true) {
- synchronized(LockClass.locka) {//a锁
- System.out.println(Thread.currentThread().getName()+"------if_locka");
- synchronized(LockClass.lockb) {//b锁
- System.out.println(Thread.currentThread().getName()+"------if_lockb");
- }
- }
- }
- }
- else{
- while(true) {
- synchronized(LockClass.lockb) {//b锁
- System.out.println(Thread.currentThread().getName()+"------else_lockb");
- synchronized(LockClass.locka) { //a锁
- System.out.println(Thread.currentThread().getName()+"------else_locka");
- }
- }
- }
- }
- }
- }
- //定义两个锁
- class LockClass{
- static Object locka = new Object();
- static Object lockb = new Object();
- }
- class DeadLock{
- public static void main(String[] args) {
- //创建2个进程,并启动
- new Thread(new LockTest(true)).start(); //线程A
- new Thread(new LockTest(false)).start(); //线程B
- }
- }
结果:程序卡住,不能继续执行
分析:两个线程A和B,A线程执行到第12行语句,线程A持有a锁,此时cpu转向执行线程B,线程B执行到21行语句,此时线程B持有b锁,因为线程A持有a锁,所以线程B不能再往下执行,同理A进程由于获取不到b锁,也不能往下执行,这就造成了死锁现象。
二、线程间的通信
其实就是多个线程在操作同一个资源,但是操作的动作不同
1、使用同步操作同一资源的示例:
- /*
- 有一个资源
- 一个线程往里存东西,如果里边没有的话
- 一个线程往里取东西,如果里面有得话
- */
- class Resource{
- private String name;
- private String sex;
- private boolean flag=false;
- public synchronized void setInput(String name,String sex) {
- if(flag){
- try{wait();}
- catch(Exception e){}//如果有资源时,等待资源取出
- }
- this.name=name;
- this.sex=sex;
- flag=true;//表示有资源
- otify();//唤醒等待
- }
- public synchronized void getOutput(){
- if(!flag) {
- try{wait();}catch(Exception e){}//如果木有资源,等待存入资源
- }
- System.out.println("name="+name+"---sex="+sex);//这里用打印表示取出
- flag=false;//资源已取出
- notify();//唤醒等待
- }
- }
- //存线程
- class Input implements Runnable{
- private Resource r;
- Input(Resource r){
- his.r=r;
- }
- public void run(){ //复写run方法
- int x=0;
- while(true) {
- if(x==0) { //交替打印张三和李四
- r.setInput("张三",".....男");
- }
- else{
- r.setInput("丽丽","_____女");
- }
- x=(x+1)%2;//控制交替打印
- }
- }
- }
- //取线程
- class Output implements Runnable{
- private Resource r;
- Output(Resource r){
- this.r=r;
- }
- public void run(){ //复写run方法
- while(true) {
- r.getOutput();
- }
- }
- }
- class ResourceDemo2{
- public static void main(String[] args) {
- Resource r = new Resource();//表示操作的是同一个资源
- new Thread(new Input(r)).start();//开启存线程
- new Thread(new Output(r)).start();//开启取线程
- }
- }
几个小问题:
1)wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
a,这些方法存在与同步中。
b,使用这些方法时必须要标识所属的同步的锁。同一个锁上wait的线程,只可以被同一个锁上的notify唤醒。
c,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
2)wait(),sleep()有什么区别?
wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,不释放锁。
3)为甚么要定义notifyAll?
因为在需要唤醒对方线程时。如果只用notify,容易出现只唤醒本方线程的情况。导致程序中的所以线程都等待。
2、JDK1.5中提供了多线程升级解决方案。
将同步synchronized替换成显示的Lock操作。将Object中wait,notify,notifyAll,替换成了Condition对象。该Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。
代码示例:
- /*
- 生产者生产商品,供消费者使用
- 有两个或者多个生产者,生产一次就等待消费一次
- 有两个或者多个消费者,等待生产者生产一次就消费掉
- */
- import java.util.concurrent.locks.*;
- class Resource{
- private String name;
- private int count=1;
- private boolean flag = false;
- //多态
- private Lock lock=new ReentrantLock();
- //创建两Condition对象,分别来控制等待或唤醒本方和对方线程
- Condition condition_pro=lock.newCondition();
- Condition condition_con=lock.newCondition();
- //p1、p2共享此方法
- public void setProducer(String name)throws InterruptedExceptio{
- lock.lock();//锁
- try{
- while(flag)//重复判断标识,确认是否生产
- condition_pro.await();//本方等待
- this.name=name+"......"+count++;//生产
- System.out.println(Thread.currentThread().getName()+"...生产..."+this.name);//打印生产
- flag=true;//控制生产\消费标识
- condition_con.signal();//唤醒对方
- }
- finally{
- lock.unlock();//解锁,这个动作一定执行
- }
- }
- //c1、c2共享此方法
- public void getConsumer()throws InterruptedException{
- lock.lock();
- try{
- while(!flag)//重复判断标识,确认是否可以消费
- condition_con.await();
- System.out.println(Thread.currentThread().getName()+".消费."+this.name);//打印消费
- flag=false;//控制生产\消费标识
- condition_pro.signal();
- }
- finally{
- lock.unlock();
- }
- }
- }
- //生产者线程
- class Producer implements Runnable{
- private Resource res;
- Producer(Resource res) {
- this.res=res;
- }
- //复写run方法
- public void run(){
- while(true) {
- try{
- res.setProducer("商品");
- }
- catch (InterruptedException e) {
- }
- }
- }
- }
- //消费者线程
- class Consumer implements Runnable{
- private Resource res;
- Consumer(Resource res) {
- this.res=res;
- }
- //复写run
- public void run(){
- while(true) {
- try{
- res.getConsumer();
- }
- catch (InterruptedException e) {
- }
- }
- }
- }
- class ProducerConsumer{
- public static void main(String[] args) {
- Resource res=new Resource();
- new Thread(new Producer(res)).start();//第一个生产线程 p1
- new Thread(new Consumer(res)).start();//第一个消费线程 c1
- new Thread(new Producer(res)).start();//第二个生产线程 p2
- new Thread(new Consumer(res)).start();//第二个消费线程 c2
- }
- }
三、停止线程
在JDK 1.5版本之前,有stop停止线程的方法,但升级之后,此方法已经过时。
那么现在我们该如果停止线程呢?
只有一种办法,那就是让run方法结束。
1、开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
如:run方法中有如下代码,设置一个flag标记。
- public void run(){
- while(flag) {
- System.out.println(Thread.currentThread().getName()+"....run"); }
- }
那么只要在主函数或者其他线程中,在该线程执行一段时间后,将标记flag赋值false,该run方法就会结束,线程也就停止了。
2、上面的1方法可以解决一般情况,但是有一种特殊情况:就是当线程处于冻结状态。就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法interrupt();
如:
- class StopThread implements Runnable{
- private boolean flag =true;
- public void run(){
- while(flag) {
- System.out.println(Thread.currentThread().getName()+"....run");
- }
- }
- public void changeFlag(){
- flag = false;
- }
- }
- class StopThreadDemo{
- public static void main(String[] args) {
- StopThread st = new StopThread();
- Thread t1 = new Thread(st);
- Thread t2 = new Thread(st);
- t1.start();
- t2.start();
- int num = 0;
- while(true) {
- if(num++ == 60) {
- t1.interrupt();//清除冻结状态
- t2.interrupt();
- st.changeFlag();//改变循环标记
- break;
- }
- System.out.println(Thread.currentThread().getName()+"......."+num);
- }
- System.out.println("over");
- }
- }
扩展小知识:
1、join方法
当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。
2、setPriority()方法用来设置优先级
MAX_PRIORITY 最高优先级10
MIN_PRIORITY 最低优先级1
NORM_PRIORITY 分配给线程的默认优先级
3、yield()方法可以暂停当前线程,让其他线程执行。