(JAVA)-(多线程)-线程的安全问题

让我们先来用一个常见的需求来进行思考:电影院三个窗口共同卖出100张票。

我们很容易想到可以用三个线程来模拟三个窗口解决这个问题

public class test {
    public static void main(String[] args) {
        MyThread t1=new MyThread("t1");
        MyThread t2=new MyThread("t2");
        MyThread t3=new MyThread("t3");
t1.start();
t2.start();
t3.start();
    }



}
 class MyThread extends Thread {
     public MyThread() {

     }

     public MyThread(String name) {
         super(name);
     }

    static int ticket=0;//代表票数,static表示三个窗口共享

    @Override
    public void run() {
while(true) {
    ticket++;
    if(ticket>100) {
        break;
    } else
    {
        System.out.println(currentThread().getName()+"正在卖第"+ticket+"张票");
    }
}
    }
}

(JAVA)-(多线程)-线程的安全问题_第1张图片

我们看看结果发现他会有重复的票,甚至有出现超出范围的票,这是因为可能在ticket在自增之后,cpu的执行权随时会被其他线程抢走,而多个线程共享一个ticket变量。

为了避免产生这种线程安全问题,提供了三种解决方案

1.同步代码块

同步代码块能将共享数据代码锁起来,当在某个线程中执行这段代码块时,该线程会获取我们指定的对象的锁,从而使得其他线程无法同时访问该代码块。

synchronized(锁对象){
操作共享数据的代码
}

1.锁默认打开,当有一个线程进去,线程自动关闭

2.代码执行完毕,线程出来,锁自动打开

3.锁对象可以是任意的,但要是相同唯一的,一般用当前文件的字节码文件对象。


 class MyThread extends Thread {
     public MyThread() {

     }

     public MyThread(String name) {
         super(name);
     }
     static int ticket=0;//代表票数

    @Override
    public void run() {
while(true) {
    synchronized (MyThread.class) {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        ticket++;
        if (ticket > 100) {
            break;
        } else {
            System.out.println(currentThread().getName() + "正在卖第" + ticket + "张票");
        }
    }
}
    }
}

2.同步方法

如果想要把一个方法里面的代码全部锁起来,就可以用同步方法

格式:

修饰符 synchronized 返回值 方法名(){};

1.同步方法是锁住方法里面的所有代码

2.锁对象不能自己指定(修饰非静态方法:this    静态:当前类的字节码文件对象)

因此当用于 线程继承Thread类 这种创造线程方法时仍然不能解决安全问题。

class MyRunable implements Runnable{
    int ticket=0;
    @Override
    public void run() {
        while(true){

            if (extracted()) break;

        }
    }

    private synchronized boolean extracted() {
        if(ticket==100)
            return true;
        else{
            ticket++;
            System.out.println(Thread.currentThread().getName()+"在卖第"+ticket+"张票");
        }
        return false;
    }
}


public class test {
    public static void main(String[] args) {
        MyRunable mr=new MyRunable();
        Thread t1=new Thread(mr);
        Thread t2=new Thread(mr);
        Thread t3=new Thread(mr);
        t1.setName("t1");
        t1.setName("t2");
        t1.setName("t3");
        t1.start();
        t2.start();
        t3.start();
    }
}

3.lock锁

lock能够进行手动的获得锁和释放锁

1.lock是接口,不能直接实例化,下文采用他的实现类Reentrantlock来实例化

2.创建lock对象要用static关键字修饰(使多个线程公共用一把锁)

3.关锁代码不能跳过,否则程序不能停止,可以使用finally代码实现

public class test {
    public static void main(String[] args) {
        MyThread t1=new MyThread("t1");
        MyThread t2=new MyThread("t2");
        MyThread t3=new MyThread("t3");
        t1.start();
        t2.start();
        t3.start();
    }

}
class MyThread extends Thread {
    public MyThread() {

    }

    public MyThread(String name) {
        super(name);
    }
    static int ticket=0;//代表票数
static Lock lock=new ReentrantLock();
    @Override
    public void run() {
 try {
        while(true) {
            lock.lock();
           
                Thread.sleep(10);
           
            ticket++;
            if (ticket > 100) {
               break;
            } else {
                System.out.println(currentThread().getName() + "正在卖第" + ticket + "张票");
            }
 } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }finally{
            lock.unlock();
}
        }
        }
    }

 4.死锁

public class test {
    public static void main(String[] args) {
        MyThread t1=new MyThread();
        MyThread t2=new MyThread();
        t1.setName("线程1");
        t2.setName("线程2");
        t1.start();
        t2.start();
    }
}
 class MyThread extends Thread{
    static Object o1=new Object();
    static  Object o2=new Object();

    @Override
    public void run() {
        while (true) {
            if ("线程1".equals(getName())) {
                synchronized (o1) {
                    System.out.println("o1锁");

                    synchronized (o2) {
                        System.out.println("o2锁");
                    }
                }
            } else if ("线程2".equals(getName())) {
                synchronized (o2) {
                    System.out.println("o2锁");
                    synchronized (o1) {
                        System.out.println("o1锁");
                    }
                }
            }
        }
    }
}

如上代码,当线程1,线程2启动后,线程一使用了o1锁,线程2使用了o2锁,当要接着向下使用时,对方的锁还没有释放,出现锁死现象

我们要记住:千万别让两个锁嵌套起来。

你可能感兴趣的:(java,python,开发语言)