java_进阶:多线程

1、线程同步的案例

需求:某电影院目前正在上映国产大片,共有100张票。而他有三个售票窗口,请设计一个程序模拟该影院售票

思路:
​ 1 定义一个类SellTicket 实习Runnbale接口,定义一个成员变量 private int tickets = 100;
​ 2 在SellTicket类中 重写run()。实现卖票:
​ A 判断票数是否大于0
​ B 售出票之后 票数减一
​ C票没有了,也可能还有人来问,所以这个地方使用死循环 让卖票行为一直持续
3 定义一个测试类,实现售票动作
​ A 创建SellTicket 类的对象
​ B 创建三个Thread类 对象,将SellTicket 类的对象传递给线程 并给线程命名
​ C 启动线程

public class SellTicket implements Runnable{
    private int ticktes = 100;
    @Override
    public void run() {
        while(true){
            if(ticktes > 0 ){
                System.out.println(Thread.currentThread().getName()+"正在出售第" + ticktes+"张票");
                ticktes--;
            }
        }
    }
}
public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket st = new SellTicket();
        //创建三个线程
        Thread t1 = new Thread(st,"1号窗口");
        Thread t2 = new Thread(st,"2号窗口");
        Thread t3 = new Thread(st,"3号窗口");
        //启动线程
        t1.start();
        t2.start();
        t3.start();

    }
}

出现每个窗口销售各自的100张票,实际生活中 售票是需要时间的 ,因此我们售卖一张票的时间为100毫秒

public class SellTicket implements Runnable{
    private int ticktes = 100;
    @Override
    public void run() {
        while(true){
            if(ticktes > 0 ){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在出售第" + ticktes+"张票");
                ticktes--;
            }
        }
    }
}

出现负票 和相同号的票

原因线程:执行的顺序是随机的

2、解决数据安全问题

出现问题的条件:
​ 1 多线程环境
​ 2 有共享数据
​ 3 有多条语句操作共享数据

如何解决线程安全问题:
​ 基本的思想:让程序没有安全问题的环境

怎么实现呢?
​ 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行。
java提供了解决的方式是使用同步代码块或同步方法:synchronized 相当于给代码加锁
可以用在代码块和方法上 分别称为同步代码块和同步方法

2.1 同步代码块

public class SellTicket implements Runnable{
    private int ticktes = 100;
    private Object obj = new Object();
    @Override
    public void run() {
        while(true){
        // 给一下代码加锁  括号中需要一个所对象(任意对象都可以充当所对象  一般情况下使用this)
            synchronized (obj){
                if(ticktes > 0 ){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在出售第" + ticktes+"张票");
                    ticktes--;
                }
            }
        }
    }
}

2.2 同步方法

在方法的声明上添加synchronized关键字

public class SellTicket implements Runnable{
    private int ticktes = 100;
   // private String s = "abc";
    @Override
    public  void run() {
        while(true){
                sellTicket();
            }
    }
    public synchronized void sellTicket(){//这才是真正存在数据安全的操作  这个就称为同步方法  这里锁对象时this
        if(ticktes > 0 ){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"正在出售第" + ticktes+"张票");
            ticktes--;
        }
    }
}

2.3 静态同步方法

public class SellTicket implements Runnable{
    private static int ticktes = 100;
   // private String s = "abc";
    @Override
    public  void run() {
        while(true){
                sellTicket();
            }
    }
    public static synchronized void sellTicket(){
        if(ticktes > 0 ){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"正在出售第" + ticktes+"张票");
            ticktes--;
        }
    }
}

静态方法的同步代码块

public class SellTicket implements Runnable{
    private static int ticktes = 100;// 共享数据应该是静态的
   // private String s = "abc";
    @Override
    public  void run() {
        while(true){
                sellTicket();
            }
    }
    public static   void sellTicket(){//这才是真正存在数据安全的操作  这个就称为同步方法  这里锁对象时this
       synchronized(SellTicket.class){
           if(ticktes > 0 ){
               try {
                   Thread.sleep(100);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println(Thread.currentThread().getName()+"正在出售第" + ticktes+"张票");
               ticktes--;
           }
       }
   }

静态的同步方法或者静态方法中的同步代码块的所对象是类名.class对象

3、 线程安全的类

StringBuffer 线程安全的可变字符序列

StringBuilder 线程不安全的可变字符序列

Vector 线程同步的

HashTable 线程同步的 是一个哈希表的实现

在实际使用时,如果不需要线程安全的实现,推荐使用与之功能相同的 但是线程不同步的实现

4、 线程的死锁的演示

死锁是我们需要规避的问题

不同的线程分别占用对方所需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就行成了线程死锁

出现死锁 不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态 无法继续

public class DeadLockDemo {
    public static void main(String[] args) {
        final  StringBuilder  s1 = new StringBuilder();
        final  StringBuilder s2 = new StringBuilder();
        new Thread(){
            @Override
            public void run() {
                synchronized (s1){
                    s2.append("A");
                    synchronized (s2){
                        s2.append("B");
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                synchronized (s2){
                    s2.append("C");
                    synchronized (s1){
                        s1.append("D");
                        System.out.println(s2);
                        System.out.println(s1);
                    }
                }
            }
        }.start();
    }
}

死锁问题的出现是一个概率时间。

死锁问题的解决:
​ 1 减少同步代码块或同步方法的嵌套
​ 2 尽量减少同步资源的定义
​ 3 使用专门的算法

你可能感兴趣的:(学习笔记,线程,java,多线程)