Java-线程

基本介绍

★程序(program)

为完成特定任务,用某种编程语言编写的一组指令的集合(代码)。

★进程

1)进程是指运行中的程序;在使用某个程序时,就启动了一个进程,操作系统就会为该进程分配内存空间。当使用另一个程序时,就启动了另一个进程,操作系统就会为该进程分配新的内存空间。

2)进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程:程序的产生(启动)、存在(执行)、消亡(停止)的过程。

★线程

1)线程是由进程创建的,是进程的一个实体;

2)一个进程可以拥有多个线程。例如:同时可以下载多个软件,每个软件下载的时候都是一个线程。

线程概念

1)单线程:同一时刻只允许执行一个线程;

2)多线程:同一时刻,可以执行多个线程;

3)并发:同一时刻,多个任务交替执行,因为处理任务速度快看似是同时执行,单核CPU实现的多任务就是并发;

4)并行:同一时刻,多任务同时执行,多核CPU可以实现并行。

获取本机CPU数量代码

package com.pero.thread_;


/**
 * 获取本机CPU数量
 *
 * @author Pero
 * @version 1.0
 */
public class GetCPUNumber {

    public static void main(String[] args) {

        Runtime runtime = Runtime.getRuntime();
        //获取本机CPU数量
        int cpuNumber = runtime.availableProcessors();
        System.out.println("本机CPU数量为:" + cpuNumber);
    }
}

线程的基本使用

1)继承Thread类(实现了Runnable接口),重写run()方法;

2)实现Runnable接口(自定义线程类),重写run()方法。

测试代码(继承Thread类)

package com.pero.thread_;

/**
 * @author Pero
 * @version 1.0
 */
public class Thread01 {
    public static void main(String[] args) throws InterruptedException {

        Cat cat = new Cat();
        cat.start();  //启动线程
        //当main线程启动一个子线程Thread-0,主线程不会阻塞会继续执行
        //打印主线程名称
        int times = 1;
        for (int i = 0; i < 8; i++) {
            System.out.println("主线程继续执行,第"+(times++)+"次,"+"主线程名称:"+Thread.currentThread().getName());
            Thread.sleep(1000);
        }

    }
}

//当一个类继承Thread类,那么该类也是一个线程类
//Thread类run()方法实现了Runnable接口的run()方法
class Cat extends Thread {
    @Override
    public void run() {  //重写run()方法,写上自己的业务逻辑
        int number = 1;
        while(true){
            //该线程每隔一秒,在控制台输出"喵喵,我是小猫咪"
            System.out.println("喵喵,我是小猫咪"+(number++)+
                    "子线程名称:"+Thread.currentThread().getName());
            //让该线程休眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (number > 8){
                break;
            }
        }

    }
}

源码分析

多线程机制:

点击Run'Thread01.main()'时就启动了一个进程,然后进入主方法(进程开启了一个main线程),在main线程中执行start()方法,再启动一个"Thread-0"线程,main线程不会阻塞会继续执行,主线程和子线程交替执行(同时执行)。使用JConsole监控线程执行情况(在程序执行过程中,点击Terminal,输入jconsole即可显示Java监视和管理控制台)。主线程内容执行完毕后,子线程依旧可以继续执行。

为什么使用start()方法来启动线程?

如果不用start()方法来启动线程而是直接调用run()方法,那么在main线程中run()方法是一个普通方法,程序会一直执行run()方法直至run方法执行完毕后再调用后面的语句,在执行run()方法时其实一直都在主线程中,而不是启动另一个子线程。start()方法是真正启动新线程来执run()方法。

1)执行start()方法

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        //对于VM创建/设置的主方法线程或“系统”组线程,不会调用此方法。
        //将来添加到该方法的任何新功能可能也必须添加到VM。
        //首先判断当前是不是新线程,如果threadStatus = 0表示是,如果threadStatus != 0,
        //表示不是新线程,抛出非法线程状态异常
        if (threadStatus != 0)    
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        //通知group该线程即将启动,以便将其添加到group的线程列表中,并减少group的未启动计数。
        group.add(this); 

        boolean started = false;
        try {
            start0();        //该方法是本地方法(native)
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
private volatile int threadStatus = 0;
void add(Thread t) {
        synchronized (this) {
            if (destroyed) {
                throw new IllegalThreadStateException();
            }
            if (threads == null) {
                threads = new Thread[4];
            } else if (nthreads == threads.length) {
                threads = Arrays.copyOf(threads, nthreads * 2);
            }
            threads[nthreads] = t;

            // This is done last so it doesn't matter in case the
            // thread is killed
            nthreads++;

            // The thread is now a fully fledged member of the group, even
            // though it may, or may not, have been started yet. It will prevent
            // the group from being destroyed so the unstarted Threads count is
            // decremented.
            nUnstartedThreads--;
        }
    }
//start0()方法是本地方法,由JVM机调用,底层由c/c++语言实现
//实现多线程的是start0()方法
private native void start0();    

//★start()方法调用start0()方法后,该线程不一定会立刻执行,只是将线程变为可运行状态
//  具体什么时间运行,取决于CPU,由CPU统一调度
void threadStartFailed(Thread t) {
        synchronized(this) {
            remove(t);
            nUnstartedThreads++;
        }
    }

测试代码(实现Runnable接口)

由于java是单继承机制,当一个类继承了一个父类后,无法在继承Thread类来创建线程,这时可以通过实现Runnable接口来创建线程。

package com.pero.thread_;

/**
 * @author Pero
 * @version 1.0
 */
public class Thread02 {
    public static void main(String[] args) {
        Tiger tiger = new Tiger();
        //tiger.start();    //Tiger类中没有start()方法,所实现的接口Runnable中也没有该方法,无法调用
        Thread thread = new Thread(tiger);    //底层使用设计模式(代理模式)
        thread.start();
    }
}
class Tiger implements Runnable{
    int count = 0;
    @Override
    public void run() {
        while (true){
            System.out.println("虎啸"+(++count)+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count == 8){
                break;
            }
        }
    }
}

源码分析

1)执行Thread(Runnable target)构造器

public Thread(Runnable target) {    //传入实现了Runnable接口的对象
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
/* What will be run. */
    private Runnable target;
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {    //线程名字
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;

        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

模拟简易的new Thread(Runnable target)机制

package com.pero.thread_;

/**
 * @author Pero
 * @version 1.0
 */

//线程代理类ThreadProxy
public class ThreadProxy implements Runnable{

    public static void main(String[] args) {
        Dog dog = new Dog();
        ThreadProxy threadProxy = new ThreadProxy(dog);
        threadProxy.start();
    }
    Runnable target = null;

    //将实现Runnable接口的对象传入构造器
    public ThreadProxy(Runnable target) {   
        this.target = target;
    }

    @Override
    public void run() {
        if (target != null){
            target.run();   //动态绑定(运行类型是Dog)
        }
    }
    
    public void start(){
        start0();   //在Thread类中实现多线程方法
    }
    
    public void start0(){
        run();
    }
}
class Dog implements Runnable{

    @Override
    public void run() {
        
    }
}

多线程执行

package com.pero.thread_;

/**
 * main方法启动两个子线程
 * @author Pero
 * @version 1.0
 */
public class Thread03 {
    public static void main(String[] args) {

        Panda panda = new Panda();
        Fish fish = new Fish();
        Thread thread = new Thread(panda);
        Thread thread1 = new Thread(fish);
        thread.start();
        thread1.start();

    }
}
class Animal{ }
class Panda extends Animal implements Runnable{

    int count = 0;
    @Override
    public void run() {
        while (true){
            System.out.println("熊猫睡懒觉"+ (++count)+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count == 80){
                break;
            }
        }
    }
}
class Fish extends Animal implements Runnable{

    int count = 0;
    @Override
    public void run() {
        while (true){
            System.out.println("鱼儿在水里游"+(++count)+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count == 100){
                break;
            }
        }
    }
}

继承Thread与实现Runnable的区别

通过继承Thread类或者实现Runnable接口来创建线程,本质上没有区别,Thread本身就实现了Runnable接口,并通过调用start()方法来执行start0()方法,从而实现run()方法的内容。实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单线程的限制。

代码测试(继承Thread类)

package com.pero.thread_;

/**
 * 模拟售票窗口(100张门票),分别用继承Tread类和实现Runnable接口方式
 *
 * @author Pero
 * @version 1.0
 */
public class SellTicket {
    public static void main(String[] args) {

        //抢占资源,票数的剩余数据出现异常
        Ticket02 ticket01 = new Ticket02();
        ticket01.start();
        Ticket02 ticket02 = new Ticket02();
        ticket02.start();
        Ticket02 ticket03 = new Ticket02();
        ticket03.start();
    }
}

class Ticket02 extends Thread {
    public static int ticketNum = 100;

    @Override
    public void run() {
        while (true) {
            if (ticketNum <= 0) {
                System.out.println("票已售完");
                break;
            }

            //如果还有票,卖出前休眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            System.out.println("窗口:" + Thread.currentThread().getName() +
                    "售出了1张票" + "剩余票数" + (--ticketNum));
        }
    }


}

代码测试(实现Runnable接口)

package com.pero.thread_;

/**
 * @author Pero
 * @version 1.0
 */
public class SellTicketRunnable {
    public static void main(String[] args) {
        SellTicket01 sellTicket01 = new SellTicket01();
        new Thread(sellTicket01).start();   //共用一个对象资源,依旧超卖
        new Thread(sellTicket01).start();
        new Thread(sellTicket01).start();
    }
}
class SellTicket01 implements Runnable{

    int count = 100;
    @Override
    public void run() {
        while (true){

            if (count <= 0){
                System.out.println("票已售罄");
                break;
            }

            //售票前休息一分钟
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            System.out.println("窗口:"+Thread.currentThread().getName()+
                    "售出1张票"+"剩余票数:"+(--count));
        }
    }
}

线程终止

1)当线程完成任务后,会自动退后。

2)还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式。

package com.pero.thread_;


/**
 * @author Pero
 * @version 1.0
 */
public class ThreadExit_ {

    public static void main(String[] args) {
        System.out.println("主线程已开启");
        T t = new T();
        new Thread(t).start();

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("主线程休眠10秒钟结束");
        //如果main线程控制其他线程终止,设置一个可修改的变量
        t.setLoop(false);
    }
}
class T implements Runnable {
    public boolean loop = true;
    private int count = 0;

    @Override
    public void run() {
        while(loop){

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            System.out.println("线程:"+Thread.currentThread().getName()+"第"+(++count)+"执行");

        }
    }

    public void setLoop(boolean loop) {
        this.loop = loop;
    }
}

线程常用方法

1)setName()方法,设置线程名称,使之与参数name相同;

2)getName()方法,返回该线程名称;

3)start()方法,使该线程开始执行,java虚拟机底层调用该线程的start0()方法;

4)run()方法,调用该线程对象run()方法;

5)setPriority()方法,更改线程的优先级;

6)getPriority()方法,获取该线程的优先级;

7)sleep()方法,在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)

8)interrupt()方法,中断线程

package com.pero.thread_;

/**
 * @author Pero
 * @version 1.0
 */
public class ThreadMethod01 {
    public static void main(String[] args) {
        Test test = new Test();
        test.setName("pero");
        test.setPriority(Thread.MIN_PRIORITY);
        test.start();

        for (int i = 0; i < 5; i++) {
            try {
                System.out.println(Thread.currentThread().getName()+"休眠10秒");
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"执行中"+i);
        }
        test.interrupt();
    }
}
class Test extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"执行中");
        }
        try {
            System.out.println(Thread.currentThread().getName()+"休眠20秒");
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+"被唤醒");
        }
    }
}

9)yield()方法,线程的礼让。让出CPU,让其他线程执行,但礼让时间不确定,所以也不一定礼让成功;本线程中的类语句中写出。

10)join()方法,线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程的所有任务;让别人插进来,在被插队的线程中的类语句中写出

yield()方法礼让代码

package com.pero.thread_;

/**
 * @author Pero
 * @version 1.0
 */
public class Test02 {
    public static void main(String[] args) throws InterruptedException {
        Test03 test03 = new Test03();
        test03.start();

        for (int i = 1; i <= 20; i++) {
            Thread.sleep(1000);
            System.out.println("主线程执行第"+i+"次");
            if (i==5){
                System.out.println("子线程插队先执行");
                //test03.join();  //插队,子线程插队,子线程先执行完毕后主线程在执行
                Thread.yield();  //礼让不一定成功,CUP资源丰富的情况下,礼让无法显现出来
                System.out.println("主线程开始执行");
            }
        }
    }
}

class Test03 extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 20; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("子线程执行了:" + i + "次");
        }
    }

}

join()方法插队代码

package com.pero.thread_;

/**
 * @author Pero
 * @version 1.0
 */
public class Test02 {
    public static void main(String[] args) throws InterruptedException {
        Test03 test03 = new Test03();
        test03.start();

        for (int i = 1; i <= 20; i++) {
            Thread.sleep(1000);
            System.out.println("主线程执行第"+i+"次");
            if (i==5){
                System.out.println("子线程插队先执行");
                test03.join();
                System.out.println("主线程开始执行");
            }
        }
    }
}

class Test03 extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 20; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("子线程执行了:" + i + "次");
        }
    }

}

用户线程和守护线程

1)用户线程:也称工作线程,线程任务执行完毕或者以通知的方式结束;

2)守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程会自动结束(常见的守护线程是垃圾回收机制)。

package com.pero.thread_;

/**
 * 当所有的用户线程结束,守护线程会自动结束
 *
 * @author Pero
 * @version 1.0
 */
public class ThreadMethod03 {
    public static void main(String[] args) throws InterruptedException {

        CycleThread cycleThread = new CycleThread();
        Thread thread = new Thread(cycleThread);
        //子线程变成守护线程,需要设置在启动线程之前
        // 当所有的用户线程结束,守护线程会自动结束
        thread.setDaemon(true);
        thread.start();

        for (int i = 1; i <= 10; i++) {
            System.out.println("主线程执行第"+i+"次");
            Thread.sleep(1000);
        }
        System.out.println("主线程执行完毕,子线程守护线程机制触发");
    }
}
class CycleThread implements Runnable{

    @Override
    public void run() {
        while (true){
            try {
                System.out.println("循环线程正在执行");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

线程的生命周期

  • 线程状态。 线程可以处于以下状态之一:
    • NEW
      尚未启动的线程处于此状态。()
    • RUNNABLE
      在Java虚拟机中执行的线程处于此状态。(Ready和Running)
    • BLOCKED
      被阻塞等待监视器锁定的线程处于此状态。
    • WAITING
      正在等待另一个线程执行特定动作的线程处于此状态。
    • TIMED_WAITING
      正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
    • TERMINATED
      已退出的线程处于此状态。

测试代码 

package com.pero.thread_;

/**
 * @author Pero
 * @version 1.0
 */
public class ThreadState {
    public static void main(String[] args) throws InterruptedException {
        Test04 test04 = new Test04();  //状态:NEW
        System.out.println(test04.getName()+"状态:"+test04.getState());


        test04.start();     //状态:RUNNABLE
        while (Thread.State.TERMINATED != test04.getState()){
            System.out.println(test04.getName()+"状态:"+test04.getState());
            Thread.sleep(500);
        }

        System.out.println(test04.getName()+"状态:"+test04.getState());   //状态:TERMINATED
    }
}
class Test04 extends Thread{
    @Override
    public void run() {
        while (true){

            for (int i = 1; i <= 10; i++) {
                System.out.println("线程:"+Thread.currentThread().getName()+"执行第"+i+"次");
                try {
                    Thread.sleep(1000);     //状态TIME_WRITING
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            break;

        }
    }
}

线程的同步

在多线程编程情况下,一些数据不允许被多个线程同时访问,此时就是用同步访问技术,保证数据在任何同一时刻,最多只能被一个线程访问,以保证数据的完整性;线程同步,也可以理解为,当有一个线程在对内存操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。

同步具体方法-Synchronized

1.同步代码块

synchronized (对象) {    //得到对象的锁才能操作同步代码
    //需要被同步的代码
}

2.synchonized可以放在方法声明中,表示整个方法为同步方法

public synchronized void m(){
    //需要被同步的代码
}

代码测试

package com.pero.thread_;

/**
 * 解决售票问题同步的解决
 *
 * @author Pero
 * @version 1.0
 */
public class SellTicketTest {
    public static void main(String[] args) {
        SellTicket02 sellTicket02 = new SellTicket02();

        new Thread(sellTicket02).start();
        new Thread(sellTicket02).start();
        new Thread(sellTicket02).start();
    }
}

class SellTicket02 implements Runnable {

    private static int ticketNumber = 100;     //共享数据(静态的)
    private boolean loop = true;

    @Override
    public void run() {    //同步方法
        while (loop) {
            sell();
        }
    }

    public synchronized void sell() {

        //因为多线程同步执行run()方法,不能够直接在run()方法上直接用synchronized修饰
        //添加一个执行动作的方法,用synchronized修饰该方法,线程进入run()方法后调用
        //本方法,就会执行同步操作
        if (ticketNumber <= 0) {
            System.out.println("售票结束");
            loop = false;
            return;     //把break改为return;
        }

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("窗口" + Thread.currentThread().getName() +
                "售出1张票,剩余票数为:" + (--ticketNumber));
    }
}

3.同步原理

多个线程同时想执行被synchronized修饰的方法或者代码块,首先访问锁(该锁放在对象之上),同一时间只有一个线程能够获得锁并执行代码块中的内容,其他线程被阻塞,待执行线程结束后,多个线程又进入锁获得的步骤(如果是公平锁则分配,如果是非公平锁则是非分配方式)。

互斥锁基本介绍:(可以在代码块上加锁,也可以在方法上加锁)

1)java中引入了对象互斥锁概念,来保证共享数据操作的完整性;

2)每个对象都对应一个可称为互斥锁的标记,用来保证在任意时刻只能有一个线程可以访问该对象;

3)关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象的任意时刻只能由一个线程访问;

4)同步的局限性:导致程序的执行效率要降低;

5)同步方法(非静态的)的锁可以时this,也可是其他对象(要求是同一个对象);

6)同步方法(静态的)的锁为当前类本身。

在方法上加锁(非静态):

public synchronized void sell() {    //同步方法,该锁在this对象中

        if (ticketNumber <= 0) {
            System.out.println("售票结束");
            loop = false;
            return;     //把break改为return;
        }

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("窗口" + Thread.currentThread().getName() +
                "售出1张票,剩余票数为:" + (--ticketNumber));
    }

在方法上加锁(静态):

//静态同步方法的锁是加在SellTicket02.class上的
    //静态方法内部实现同步代码块,锁是加在SellTicket02.class对象上的
    public /*synchronized*/ static void synchronizedAction(){
        synchronized (SellTicket02.class){
            
        }
    }

在代码块上加锁:

    Object object = new Object();
    public /*synchronized*/ void sell() {    

        //同步代码块,所在this对象上(也可以在别的同一个对象上例如object,但是不能再多个对象上)
        synchronized(/*this*/ object) {    
            if (ticketNumber <= 0) {
                System.out.println("售票结束");
                loop = false;
                return;     //把break改为return;
            }

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            System.out.println("窗口" + Thread.currentThread().getName() +
                    "售出1张票,剩余票数为:" + (--ticketNumber));
        }
    }

注意细节:

1)同步方法如果没有使用static修饰,默认锁对象this;

2)如果方法使用static修饰,则默认锁对象为当前类.class

3)实现的落地步骤:①需要先分析上锁的代码;②选择同步代码块(效率相对来说比较高)或者同步方法;③要求多个线程的锁对象为同一个。

线程的死锁

多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程时要避免死锁的发生。

package com.pero.thread_;

/**
 * 模拟死锁
 *
 * @author Pero
 * @version 1.0
 */
public class DeadLock_ {
    public static void main(String[] args) {

    }
}

class DeadLockDemo extends Thread {
    static Object object01 = new Object();
    static Object object02 = new Object();

    boolean flag;

    public DeadLockDemo(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {

        //存在问题:
        // 当一个线程中flag判断结果为true,线程持有object01的互斥锁,
        //但是进入同步代码块的执行语句时,如果object02的互斥锁没有得到就会停滞在
        //BLOCKED状态(阻塞中)。
        //当另外一个线程中flag结果判定为false,线程持有object02的互斥锁,
        //但是进入同步代码块的执行语句时,如果object02的互斥锁没有得到就会停滞在
        //BLOCKED状态(阻塞中)。
        //获得锁的线程无法执行完成代码内容,无法返回锁,则两个线程都会一直停滞在阻塞状态
        if (flag) {
            synchronized (object01) {   //同步代码,对象互斥锁
                System.out.println(Thread.currentThread().getName() + "进入1");
                synchronized (object02) {
                    System.out.println(Thread.currentThread().getName() + "进入2");
                }
            }
        }else {
            synchronized (object02){
                System.out.println(Thread.currentThread().getName() + "进入3");
                synchronized (object01){
                    System.out.println(Thread.currentThread().getName() + "进入4");
                }
            }
        }
    }
}

释放锁的情况

1)当线程的同步方法、同步代码执行结束后会释放锁;

2)当线程在同步代码块、同步方法中遇到break和return会释放锁;

3)当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束会释放锁;

4)当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停并释放锁。

不会释放锁的情况

1)当线程执行同步代码块或者同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁;

2)线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值