实现java的多线程操作——线程的同步与死锁(七)

线程的同步与死锁

1.1同步问题的引出
如果要想进行同步的操作,那么和明细就是多个线程需要访问统一资源
范例:以卖票为例

package day1;

class NewThread implements Runnable{//表示实现多线程
    private int ticket = 5;
    public void run() {//覆写run(),线程的主方法
         for (int i = 0;i < 10; i++){
          if(this.ticket > 0){
             System.out.println(Thread.currentThread().getName() + "=" + this.ticket--);
         }
     }
    }
}
public class TestRunnableDemo {
    public static void main(String[] args) throws Exception {
      NewThread myThread = new NewThread();
        Thread t1 = new Thread(myThread,"线程A");//线程启动调用run()方法
        Thread t2 = new Thread(myThread,"线程B");//线程启动调用run()方法
        Thread t3 = new Thread(myThread,"线程C");//线程启动调用run()方法
        t1.start();
        t2.start();
        t3.start();
    }
}

线程A=5
线程C=3
线程B=4
线程C=1
线程A=2

于是下面开始挖掘本程序所存在的问题。

范例:观察程序的问题

package day1;

class NewThread implements Runnable{//表示实现多线程
    private int ticket = 5;
    public void run() {//覆写run(),线程的主方法
         for (int i = 0;i < 10; i++){
          if(this.ticket > 0){
              try {
                  Thread.sleep(100);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println(Thread.currentThread().getName() + "=" + this.ticket--);
         }
     }
    }
}
public class TestRunnableDemo {
    public static void main(String[] args) throws Exception {
      NewThread myThread = new NewThread();
        Thread t1 = new Thread(myThread,"线程A");//线程启动调用run()方法
        Thread t2 = new Thread(myThread,"线程B");//线程启动调用run()方法
        Thread t3 = new Thread(myThread,"线程C");//线程启动调用run()方法
        t1.start();
        t2.start();
        t3.start();
    }
}
线程A=5
线程B=5
线程C=4
线程C=3
线程B=2
线程A=1
线程B=0
线程C=-1

此时就观察出程序的问题所在了,出现了负数。
在这里插入图片描述

这样的 问题就属于线程的不同步操作,所以发现多个线程操作时必须要考虑资源的同步问题。

1.2 实现同步操作
整个代码之中发现一个逻辑上的错误。以上程序中,将判断是否有票、延迟、买票分为了三个部分。那么实际上每一个线程如果要执行卖票,其他线程应该等待当前线程执行完毕后才可以进入。解决问题
在这里插入图片描述
如果要想在若干行代码上实现锁这个概念,那么就需要通过使用同步代码块或同步方法解决。

1.同步代码块
使用synchronized关键字定义的代码块就称为同步代码块,但是在进行同步的时候需要设置有一个同步对象,那么往往可以使用this同步当前对象。

package day1;

class NewThread implements Runnable{//表示实现多线程
    private int ticket = 50;
    public void run() {//覆写run(),线程的主方法
         for (int i = 0;i < 100; i++){
             synchronized (this){
                 if(this.ticket > 0){
                     try {
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     System.out.println(Thread.currentThread().getName() + "=" + this.ticket--);
                 }
             }
         }
    }
}
public class TestRunnableDemo {
    public static void main(String[] args) throws Exception {
      NewThread myThread = new NewThread();
        Thread t1 = new Thread(myThread,"线程A");//线程启动调用run()方法
        Thread t2 = new Thread(myThread,"线程B");//线程启动调用run()方法
        Thread t3 = new Thread(myThread,"线程C");//线程启动调用run()方法
        t1.start();
        t2.start();
        t3.start();
    }
}

加入同步之后整个的代码执行的速度已经变慢了,而且不像没有同步的时候那样,多个线程会一起进入到方法之中。
异步的执行速度要快于同步的执行速度,但是异步的操作属于非线程安全的操作,而同步操作属于线程的安全操作。

2.同步方法
但是对于同步操作,除了了用于代码块定义外,也可以在方法上定义同步操作。

package day1;

class NewThread implements Runnable{//表示实现多线程
    private int ticket = 50;
    public void run() {//覆写run(),线程的主方法
        for (int i = 0;i < 100; i++) {
            this.sall();
        }
    }
    public synchronized void sall(){
            if(this.ticket > 0){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "=" + this.ticket--);
            }
        }
    }
public class TestRunnableDemo {
    public static void main(String[] args) throws Exception {
      NewThread myThread = new NewThread();
        Thread t1 = new Thread(myThread,"线程A");//线程启动调用run()方法
        Thread t2 = new Thread(myThread,"线程B");//线程启动调用run()方法
        Thread t3 = new Thread(myThread,"线程C");//线程启动调用run()方法
        t1.start();
        t2.start();
        t3.start();
    }
}

在多个线程访问同一资源时一定要考虑到数据同步问题,同步就使用synchronize关键字。

1.3 死锁分析
死锁是一种不确定的状态,对于死锁的操作应该出现的越少越好,下面的代码只是一个死锁的演示,代码不做任何的实际意义。

package day1;

class QiangDao{
    public synchronized void say(YouQianRen yqr){
        System.out.println("强盗说:给我3000万美金,还你儿子。");
        yqr.get();
    }
    public synchronized void get(){
        System.out.println("强盗得到了3000万美金放了那个还在。");
    }
}
class YouQianRen{
    public synchronized void say(QiangDao qd){
        System.out.println("有钱人:先放了我儿子,在考虑给你3000万美金。");
        qd.get();
    }
    public synchronized void get(){
        System.out.println("有钱人找回了儿子。");
    }
}
public class TestDemo implements Runnable{
    private QiangDao qd = new QiangDao();
    private YouQianRen yqr = new YouQianRen();

    public TestDemo(){
        new Thread(this).start();
        yqr.say(qd);
    }
    public static void main(String[] args) {
        new TestDemo();
    }
    @Override
    public void run() {
        qd.say(yqr);
    }
}

输出
在这里插入图片描述
出现死锁,程序没有执行完。

面试题:请问多个线程访问同意资源是可能带来什么问题?以及会产生什么样的附加问题?
1.多个线程访问统一资源是必须要考虑同步,可以使用synchronize定义同步代码块或同步方法;
2.程序中如果出现过多的同步那么就将产生死锁。

总结:如果看到synchronize声明的方法,一定要记住,这是一个同步方法,属于线程安全的操作,但是性能不会特别的高。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值