线程的生命周期和互斥锁基础

58 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第26天,点击查看活动详情

用户线程和守护线程

(1)用户线程:也叫工作线程,当线程的任务执行完或通知方式结束

(2)守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束

(3)常见的守护线程:垃圾回收机制

线程的生命周期

JDK中用Thread.State枚举表示了线程的几种状态

Thread.State (Java Platform SE 6)

  • public static enum Thread.State
    extends Enum<Thread.State>
    

线程状态。线程可以处于下列状态之一:

  • NEW
    至今尚未启动的线程处于这种状态。
  • RUNNABLE
    正在 Java 虚拟机中执行的线程处于这种状态。
  • BLOCKED
    受阻塞并等待某个监视器锁的线程处于这种状态。
  • WAITING
    无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
  • TIMED_WAITING
    等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
  • TERMINATED
    已退出的线程处于这种状态。

在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。

写程序查看线程状态

public class ThreadState_ {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        System.out.println(t.getName() + " 状态 " + t.getState());
        t.start();
        while (Thread.State.TERMINATED != t.getState()) {
            System.out.println(t.getName() + " 状态 " + t.getState());
            Thread.sleep(500);
        }
        System.out.println(t.getName() + " 状态 " + t.getState());
    }
}

class T extends Thread {
    @Override
    public void run() {
        while (true) {
            for (int i = 0; i < 10; i++) {
                System.out.println("hi " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            break;
        }
    }
}

Synchronized

线程同步机制

(1)在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据

在任何同一时刻,最多有一个线程访问,以保证数据的完整性。

(2)也可以这样理解:线程同步,即当有一个线程在对内存进行操作时,其它线程都不可以对这个内

存地址进行操作,直到该线程完成操作,其它线程才能对该内存地址进行操作。

同步具体方法

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

互斥锁

基本介绍

(1)Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。

(2)每个对象都对应一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访

问该对象。

(3)关键字synchronized用来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在

任一时刻只能有一个线程访问。

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

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

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

使用互斥锁来解决售票问题

public class SellTicket {
    public static void main(String[] args) {
        //测试
        // SellTicket01 sellTicket01 = new SellTicket01();
        // SellTicket01 sellTicket02 = new SellTicket01();
        // SellTicket01 sellTicket03 = new SellTicket01();
        
        // //这里我们会出现超卖.. 
        // sellTicket01.start();//启动售票线程
        // sellTicket02.start();//启动售票线程
        // sellTicket03.start();//启动售票线程
        // System.out.println("===使用实现接口方式来售票=====");
        // SellTicket02 sellTicket02 = new SellTicket02();
        
        // new Thread(sellTicket02).start();//第 1 个线程-窗口
        // new Thread(sellTicket02).start();//第 2 个线程-窗口
        // new Thread(sellTicket02).start();//第 3 个线程-窗口
        
        //测试
        SellTicket03 sellTicket03 = new SellTicket03();
        new Thread(sellTicket03).start();//第 1 个线程-窗口
        new Thread(sellTicket03).start();//第 2 个线程-窗口
        new Thread(sellTicket03).start();//第 3 个线程-窗口
    }
}

//实现接口方式, 使用 synchronized 实现线程同步
class SellTicket03 implements Runnable {
    private int ticketNum = 100;//让多个线程共享 ticketNum
    private boolean loop = true;//控制 run 方法变量
    Object object = new Object();
    
    //同步方法(静态的)的锁为当前类本身
    //1. public synchronized static void m1() {} 锁是加在 SellTicket03.class
    //2. 如果在静态方法中,实现一个同步代码块. 
    /*
    synchronized (SellTicket03.class) {
        System.out.println("m2");
    }
    */
    
    public synchronized static void m1() {
    }
    public static void m2() {
        synchronized (SellTicket03.class) {
            System.out.println("m2");
        }
    }

    //1. public synchronized void sell() {} 就是一个同步方法
    //2. 这时锁在 this 对象
    //3. 也可以在代码块上写 synchronize ,同步代码块, 互斥锁还是在 this 对象
    public /*synchronized*/ void sell() { //同步方法, 在同一时刻, 只能有一个线程来执行sell方法
        synchronized (/*this*/ object) {
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                loop = false;
                return;
            }
            //休眠 50 毫秒, 模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));//1 - 0 - -1 - -2
        }
    }
    @Override
    public void run() {
        while (loop) {
            sell();//sell 方法是一种同步方法
        }
    }
}

//使用 Thread 方式
// new SellTicket01().start()
// new SellTicket01().start();
class SellTicket01 extends Thread {
    private static int ticketNum = 100;//让多个线程共享 ticketNum
    // public void m1() {
        // synchronized (this) {
            // System.out.println("hello");
        // }
    // }

    @Override
    public void run() {
        while (true) {
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                break;
            }
            //休眠 50 毫秒, 模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));
        }
    }
}
    
    //实现接口方式
class SellTicket02 implements Runnable {
    private int ticketNum = 100;//让多个线程共享 ticketNum
    @Override
    public void run() {
        while (true) {
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                break;
            }
            //休眠 50 毫秒, 模拟
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" + " 剩余票数=" + (--ticketNum));//1 - 0 - -1 - -2
        }
    }
}

注意事项和细节

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

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

(3)实现的落地步骤:

1)需要先分析上锁的代码

2)选择同步代码块或同步方法

3)要求多个线程的锁对象为同一个即可