java多线程2

我们知道,线程有五种生命周期:新建,就绪,运行,阻塞,死亡.

在我们编写,运行代码的过程中,可能出现线程死锁,线程阻塞等问题,下面介绍线程产生这些问题的原因,及解决的方案,保证线程能正常运行.

一、线程阻塞

线程阻塞的原因

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,需要等待上一个释放

 

posted @ 2022-08-28 22:57  Tmillion  阅读(40)  评论(0)    收藏  举报