Java高级编程——多线程(生命周期+线程同步)

1、线程的生命周期

1.1、线程的五种状态

  • JDK中用Thread.State类定义了线程的几种状态

新建:当一个Thraed类或其子类对象被声明并创建时,新生的线程对象处于新建状态

就绪:新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是还没有分配到CPU。

运行:就绪态的进程被调度并获得CPU,遍进入运行状态,run()方法定义了线程的操作和功能

阻塞:在特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态

死亡:线程完成了它的全部工作被提前强制性的终止或出现异常导致结束

1.2、五种状态的切换关系

在这里插入图片描述

2、线程的同步(重点)

为了解决线程的安全问题

2.1 问题的提出——多窗口售票问题

重票
在这里插入图片描述
错票
在这里插入图片描述
分析:

 * 1、买票过程中,出现重票、错票--->出现了线程安全问题
 * 2、问题出现的原因:当耨个线程操作车票的过程中,尚未完成操作时,其他线程也参与进来,操作车票
 * 3、解决:当一个线程在操作ticket的时候其他线程不能参与,直到当前线程的操作结束.最终期刊即使当前线程出现了阻塞也不能被改变
  • 多个线程执行的不确定性引起执行结果的不确定性
  • 多个线程堆共享数据的操作

2.2 java中通过线程同步机制解决线程安全问题

2.2.1 方式一:同步代码块 synchronized()

2.2.1.1 同步代码块解决实现Runnable接口类的线程安全问题
synchronized(同步监视器--同步锁对象){
		//需要被同步的代码,操作共享的核心资源
}
说明:1、操作共享数据的代码即为需要同步的代码
	 2、同步监视器(),任何一个类的对象都可以充当锁
		要求:多个线程必须共用**同一把锁**

修改实现类代码:

class Window1 implements  Runnable{

    private  int ticket = 100;
    //1、新建任意类的对象
    Object obj = new Object();


    @Override
    public void run() {
        while (true) {
        //2、给操作共享数据的代码加锁
            synchronized (obj) {
                if (ticket > 0) {

                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket--;
                    System.out.println(Thread.currentThread().getName() + "窗口售出一张票,当前余票" + (ticket));
                } else break;

            }
        }
    }
}

补充:这里的同步监视器可以是任何类的对象且唯一,说明这里的参数也可以是当前的对象本身,即此处的参数可以为(this)

            synchronized(this){//synchronized (obj) {
                if (ticket > 0) {
                    ticket--;
                    System.out.println(getName() + "窗口售出一张票,当前余票" + (ticket));
                } else break;
            }
2.2.1.2 同步代码块解决实现Thread类的线程安全问题

子类代码:

class Window extends  Thread{

    private static int ticket = 100;
    //1、为了保证“锁”时同一个,座椅子类需要static修饰
    private static Object obj  = new Object();
    Window(){}

    Window(String name){
        super(name);
    }
    @Override
    public void run() {
        while (true) {
            //2、加锁
            synchronized (obj) {
                if (ticket > 0) {
                    ticket--;
                    System.out.println(getName() + "窗口售出一张票,当前余票" + (ticket));

                } else break;
            }
        }
    }
}

补充:这里的同步监视器可以是(当前类类名.class)

            synchronized (Window.class) {//类也是对象,且类只会加载一次
                if (ticket > 0) {
                    ticket--;
                    System.out.println(getName() + "窗口售出一张票,当前余票" + (ticket));
                } else break;
            }

2.2.2 方式二:同步方法 (method)

如果操作共享数据的代码完整的声明在一个方法中,我们可以将此方法同步

2.2.2.1 同步方法解决实现Runnable接口类的线程安全问题

实现类:

class Thread2 implements  Runnable{

    private  int ticket = 100;


    @Override
    public void run() {
        while (true) {
            show();
        }
    }


    private  synchronized void show(){//此时的同步监视器this
        if (ticket > 0) {

            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticket--;
            System.out.println(Thread.currentThread().getName() + "窗口售出一张票,当前余票" + (ticket));

        }
    }

}
2.2.2.2 同步方法解决实现Thread类的线程安全问题
class Window1 extends  Thread{

    private static int ticket = 100;

    Window1(){}

    Window1(String name){
        super(name);
    }
    @Override
    public void run() {
        while (true) {
            show();
        }
    }

    private static synchronized   void show(){//同步监视器 Window1.class
        if (ticket > 0) {

            try {
                sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticket--;
            System.out.println(Thread.currentThread().getName() + "窗口售出一张票,当前余票" + (ticket));
        }
    }

}

2.2.3 同步方法的总结

* 同步方法仍涉及到同步监视器,只是不需要显示地声明
*static的同步方法,同步监视器是this
* static的同步方法,监视器是当前类本身(Xxxx.class

2.2.4 同步的方式的优缺点

优点:解决了线程安全问题
不足:操作同步代码时,其他线程无区别等待。相当于一个单线程,效率低

2.2.5 同步方法的两个问题——

2.2.5.1 改写单例模式(懒汉式)为线程安全
class  Bank{
    private Bank(){}


    private static  Bank instance = null;


    public static  Bank getInstance(){
//方式一:效率较差
/*        synchronized (Bank.class){
            if (instance == null) {
                instance  = new Bank();
            }
            return instance;
        }*/


//方式二:
        if(instance == null ){
            synchronized (Bank.class){
                if (instance == null) {
                    instance  = new Bank();
                }

            }
        }
        return instance;
    }
}
2.2.5.2 死锁问题

出现死锁的原因

  • 不同的线程分别占据对方需要的同步资源不放弃,都在等待对方放弃自己需要的资源,就形成了线程额死锁
  • 出现死锁后不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态没无法继续

死锁代码举例

public class ThreadTest {

    public static void main(String[] args) {

        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();


        new Thread(){
            @Override
            public void run() {
                synchronized(s1){
                    s1.append("a");
                    s2.append("1");


                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s2){
                        s1.append("b");
                        s2.append("2");


                        System.out.println(s1);;
                        System.out.println(s2);
                    }
                }
            }
        }.start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(s2){
                    s1.append("C");
                    s2.append("3");

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s1){
                        s1.append("D");
                        s2.append("4");


                        System.out.println(s1);;
                        System.out.println(s2);
                    }
                }
            }
        }).start();

    }

}

解决死锁的办法

* 专门的算法
* 尽量减少同步资源的定义
* 尽量避免嵌套同步

2.3解决线程安全问题——方式三lock锁

  • 为了清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,更加灵活方便。
  • Lock实现提供比synchronized方法和语句可以获得更广泛的锁定操作
  • Lock不能直接实例化,这里采用他的实现类ReentrantLock来构建Lock锁对象
public class LockTest {

    public static void main(String[] args) {
        Window w1 = new Window();
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);

        t1.setName("窗口一");
        t2.setName("窗口二");
        t3.setName("窗口三");

        t3.start();
        t2.start();
        t1.start();
    }




}

class  Window implements Runnable{

    private int ticket = 100;

    //实例化ReentrantLock
    private final  ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {
                //调用lock()方法
                lock.lock();
                if (ticket > 0) {
                    try {
                        sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":售票,票号为:"+ticket);
                    ticket--;
                }else break;
            }finally {
                //3.调用unlock()解锁方法
                lock.unlock();
            }
        }
    }
}

2.4 synchronized 与 lock的异同

2.4.1 同

都可以解决线程安全问题

2.4.2 异

synchronized:执行完相应的同步代码后,自动释放同步监视器
Lock需要手动启动同步lock(),结束时也需要手动实现(unlock())

优先使用顺序

Lock >> 同步代码块 >> 同步方法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值