线程安全

卖票案例:

  1. 定义一个类Ticket实现Runnable接口,定义成员变量:privite int ticketCount=100;
  2. 在Ticket类中重写run()方法实现卖票,代码步骤如下:
    • 判断票数大于0,就卖票,并告知是哪个窗口卖的。
    • 票数-1
    • 卖光,线程截至。
  3. 定义测试类TicketDemo,main方法代码步骤如下:
    • 创建Ticket类的对象
    • 创建三个Thread类的对象,把Ticket对象作为构造参数,并给出对应的窗口名称。
    • 启动线程

 实际生活中卖票需要时间,所以出售一张票时,需要一点时间延迟,用sleep()方法实现。

  卖票出现问题:相同的票卖了多次;出现了负数票。

卖票安全问题解决

  为什么出现问题?--多线程操作数据

  如何解决?--让程序没有安全问题的环境

  怎么实现?--把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。

        同步代码块解决。

同步代码块:

  格式:synchronized(任意对象){

        多条语句操作共享数据的代码;

      }

  默认情况是打开的,只要有一个线程进去执行代码,锁就会关闭。

  单线程执行完毕,锁才会自动打开。

  好处和弊端:

    好处:解决多线程数据安全问题

    弊端:线程很多时,每个线程都会判断同步上的锁,很耗费资源,降低程序运行效率。

public class Ticket implements Runnable {
    //票数
    private int ticket = 100;
    private Object obj=new Object();

    @Override
    public void run() {
        while (true) {
            //多个线程必须使用同一把锁
            synchronized (obj){
                if (ticket <= 0) {
                    //卖完了
                    break;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket--;
                    System.out.println(Thread.currentThread().getName() + "卖出1张,剩余票数:" + ticket);
                }
            }
        }
    }
}

  

public class Demo {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        Thread thread3 = new Thread(ticket);

        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();

    }
}

 

同步方法

  一、同步方法:就是把synchronized关键字加到方法上。

    格式:

      修饰符 synchronized返回值类型 方法名(方法参数){ }

  同步代码块与同步方法的区别:

    同步代码块可以锁住指定代码,同步方法是锁住方法中的所有代码。

    同步代码块可以指定锁对象,同步方法不能指定锁对象。

    同步方法的锁对象是:this

public class MyRunnable implements Runnable {
    private int ticketCount = 100;

    @Override
    public void run() {
        while (true) {
            if ("窗口一".equals(Thread.currentThread().getName())) {
                //同步方法实现
                boolean result = synchronizedMethod();
                if (result) {
                    break;
                }
            }
            if ("窗口二".equals(Thread.currentThread().getName())) {
                //同步代码块实现
                synchronized (this) {
                    if (ticketCount == 0) {
                        break;
                    } else {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        ticketCount--;
                        System.out.println(Thread.currentThread().getName() + "卖票,还剩下" + ticketCount + "张");
                    }
                }
            }
            ticketCount--;
            System.out.println(Thread.currentThread().getName() + "卖票,还剩下" + ticketCount + "张");
        }
    }

    private synchronized boolean synchronizedMethod() {
        if (ticketCount == 0) {
            return true;
        } else {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticketCount--;
            System.out.println(Thread.currentThread().getName() + "卖票,还剩下" + ticketCount + "张");
            return false;
        }
    }
}

  

public class Demo {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(myRunnable);

        thread1.setName("窗口一");
        thread2.setName("窗口二");

        thread1.start();
        thread2.start();
    }
}

  二、同步静态方法:就是把synchronized关键字加到静态方法上

    格式:

      修饰符 static synchronized 返回值类型 方法名(方法参数){   }

    同步静态方法的锁对象是:类名.class

package com.itheima.threaddemo11;

public class MyRunnable implements Runnable {
    private static int ticketCount = 100;

    @Override
    public void run() {
        while (true) {
            if ("窗口一".equals(Thread.currentThread().getName())) {
                //同步方法实现
                boolean result = synchronizedMethod();
                if (result) {
                    break;
                }
            }
            if ("窗口二".equals(Thread.currentThread().getName())) {
                //同步代码块实现
                synchronized (MyRunnable.class) {
                    if (ticketCount == 0) {
                        break;
                    } else {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        ticketCount--;
                        System.out.println(Thread.currentThread().getName() + "卖票,还剩下" + ticketCount + "张");
                    }
                }
            }
            ticketCount--;
            System.out.println(Thread.currentThread().getName() + "卖票,还剩下" + ticketCount + "张");
        }
    }

    private static synchronized boolean synchronizedMethod() {
        if (ticketCount == 0) {
            return true;
        } else {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticketCount--;
            System.out.println(Thread.currentThread().getName() + "卖票,还剩下" + ticketCount + "张");
            return false;
        }
    }
}

  

public class Demo {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(myRunnable);

        thread1.setName("窗口一");
        thread2.setName("窗口二");

        thread1.start();
        thread2.start();
    }
}

  

lock锁

  lock实现提供比使用synchronized方法和语句更广泛的锁定操作。lock中提供了获得锁和释放锁。

    void lock():获得锁

    void lock():释放锁

  lock是接口不能实例化,需要通过实现类ReentantLock实例化。

  ReentrantLock的构造方法:

    ReentrantLock():创建一个ReentrantLock的实例。

package com.itheima.threaddemo12;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 定义一个类Ticket实现Runnable接口,定义成员变量:privite int ticketCount=100;
 * 在Ticket类中重写run()方法实现卖票,代码步骤如下:
 * 判断票数大于0,就卖票,并告知是哪个窗口卖的。
 * 票数-1
 * 卖光,线程截至。
 * 定义测试类TicketDemo,main方法代码步骤如下:
 * 创建Ticket类的对象
 * 创建三个Thread类的对象,把Ticket对象作为构造参数,并给出对应的窗口名称。
 * 启动线程
 */
public class Ticket implements Runnable {
    //票数
    private int ticket = 100;
    private Object obj = new Object();
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            //多个线程必须使用同一把锁
            //synchronized (obj){
            try {
                lock.lock();
                if (ticket <= 0) {
                    //卖完了
                    break;
                } else {
                    Thread.sleep(100);
                    ticket--;
                    System.out.println(Thread.currentThread().getName() + "卖出1张,剩余票数:" + ticket);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();//一般放到finally中保证锁释放
            }
        }
    }
}

  

package com.itheima.threaddemo12;

public class Demo {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        Thread thread1 = new Thread(ticket);
        Thread thread2 = new Thread(ticket);
        Thread thread3 = new Thread(ticket);

        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();

    }
}

 

死锁

  指两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法往前执行。

  解决方法:不要写锁嵌套

package com.itheima.threaddemo13;

public class Demo {
    public static void main(String[] args) {
        Object objA=new Object();
        Object objB=new Object();
        new Thread(()->{
            while (true){
                synchronized (objA){
                    synchronized (objB) {
                        System.out.println("小康同学在走路");
                    }
                }
            }
        }).start();
        new Thread(()->{
            while (true){
                synchronized (objB){
                    synchronized (objA) {
                        System.out.println("小薇同学在走路");
                    }
                }
            }
        }).start();
    }
}

  

posted @ 2022-05-10 22:12  格兰芬多剁剁剁  阅读(30)  评论(0)    收藏  举报