线程--线程生命周期、Synchronized

四、线程的生命周期

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

  • NEW:尚未启动对啊线程处于此状态
  • RUNNABLE:在Java虚拟机中执行的线程处于此状态(可运行状态,但不一定马上running,什么时候运行需要看操作系统)
  • BLOCKED:被阻塞等待监视器锁定的线程处于此状态
  • WAITING:正在等待另一个线程执行特定动作的线程处于此状态
  • TIMED_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
  • TERMINATED:已退出的线程处于此状态

线程状态转换图

![线程 4.1](D:\桌面\博客\picture\线程 4.1.png)

五、Synchronized

5.1 线程同步

线程同步机制:

  1. 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性
  2. 也可以这样理解:线程同步,即当有一个线程对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该地址内存进行操作

5.2 同步具体实现方法

  1. 同步代码块

    synchronized (对象){//得到对象的锁,才能操作同步代码
    	//需要被同步代码;
    }
    
  2. synchronized还可以放在方法声明中,表示整个方法-为同步方法

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

售票问题改进:

public class SellTicket{
    public static void main(String[] args){
        
         SellTicket02 sellTicket02 = new SellTicket02();
        
        new Thread(sellTicket02).start();//第1个线程-窗口
        new Thread(sellTicket02).start();//第2个线程-窗口
        new Thread(sellTicket02).start();//第3个线程-窗口
        
    }
}

//使用Thread方式
class SellTicket01 extends Thread{
    private static int ticketNum = 100;//让多个线程共享ticketNum
    
    private boolean loop = true;//控制run方法变量
    
    
    public synchronized void sell(){
        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);
    }
    
    @Override
    public void run(){
        while(loop){
            
            sell();
        }
    }
}

5.3 互斥锁

同步原理分析:

![线程 5.3.1](D:\桌面\博客\picture\线程 5.3.1.png)

  1. 锁是在对象上,而非代码上
  2. Synchronized是非公平锁,谁抢到算谁的

互斥锁:

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

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

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

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

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

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

用互斥锁解决售票问题

public class SellTicket{
    public static void main(String[] args){
        
         SellTicket02 sellTicket02 = new SellTicket02();
        
        new Thread(sellTicket02).start();//第1个线程-窗口
        new Thread(sellTicket02).start();//第2个线程-窗口
        new Thread(sellTicket02).start();//第3个线程-窗口
        
    }
}


class SellTicket02 extends Thread{
    private static int ticketNum = 100;//让多个线程共享ticketNum
    private boolean loop = true;//控制run方法变量
    
    Object object = new Object();
    
    
    //同步方法(静态的)的锁为当前类本身
    //1.public synchronized static void m1() 锁是加在SellTicket02.class
    //2.如果在静态方法中,实现一个同步代码块,要用class
    public synchronized static void m1(){
        
    }
    public static void m2(){
        
        synchronized(SellTicket02.class){
            System.out.println("m2");
        }
        
        /*此时如果像下面这样会报错,因为是静态方法
        synchronized(this){
        	System.out.println("m2");
        	}
        */
    }
    
    //说明:
    //1.public synchronized void sell(){} 就是一个同步方法
    //2.这时锁在 this 对象
    //3.也可以在代码块上写synchronized ,同步代码块,互斥锁还是在 this对象
    public /*synchronized*/ void sell(){//同步方法
        
        synchronized(/*this*/ object){//如果在SellTicket02中new Object,那么this换成object也可以(三个线程操作的是一个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);
        }
        
    }
    
    @Override
    public void run(){
        while(loop){
            
            sell();
        }
    }
}

注意事项和细节:

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

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

  3. 实现的落地步骤:

    • 需要先分析上锁的代码

    • 选择同步代码块或同步方法

    • 要求多个线程的锁对象为同一个

5.4 线程死锁

多个线程都占用了对象的锁空间,但不肯相让,导致了死锁,在编程中一定要避免死锁发生

例如,小明a->b才能完成工作,小黄b->a才能完成工作,都占用了对方

模拟线程死锁

public class DeadLock{
    public static void main(String[] args){
        //模拟死锁现象
        DeadLockDemo A = new DeadLockDemo(true);
        DeadLockDemo B = new DeadLockDemo(false);
        A.start();
        B.start();
    }
}


//线程
class DeadLockDemo extends Thread {
    static Object o1 = new Object();//保证多线程,共享一个对象,这里使用static
    static Object o2 = new Object;
    boolean flag;
    
    public DeadLockDemo(boolean flag){//构造器
    	this.flag =flag;
    }
    
    @Override
    public void run(){
        
        //下面业务逻辑的分析
        //1.如果flag为 T,线程 A 就会先得到/持有 o1 对象锁,然后尝试获取 o2 对象锁
        //2.如果线程A 得不到 o2 对象锁,就会 Blocked
        //3.如果flag 为 F,线程 B 就会先得到/持有 o2 对象锁,然后尝试获取 o1 对象锁
        //4.如果线程B 得不到 o1 对象锁,就会 Blocked
        if(flag){
            synchronized (o1){//对象互斥锁,下面就是同步代码
                System.out.println(Thread.currentThread().getName() +  +"进入1");
            	synchronized(o2){//这里获得 li 对象的监视权
            		System.out.println(Thread.currentThread0.getName +"进入2");
                }
            }
        } else{
            synchronized (o2){
            	System.out.println(Thread.currentThread().getName() + "进入3");
                synchronized (o1){//这里获得li对象的监视权
                	System.out.println(Thread.currentThread0.getName( +"进入4");
                }
             }
         }
	}
}
posted @ 2025-10-19 22:50  lu璐  阅读(9)  评论(0)    收藏  举报