多线程——线程的安全问题

1、例子:创建个窗口卖票,总票数为100张.使用实现Runnable接口的方式

1.问题:卖票过程中,出现了重票、错票 -->出现了线程的安全问题
2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。
3.如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。
4.通过同步机制来解决线程安全的问题

2、Java解决方案:同步机制(Run)
2.1、同步代码块synchronized
synchronized(同步监视器){
	// 需要被同步的代码
}

说明:
①:操作共享数据的代码,即为共享代码;
②:共享数据:多个线程共同操作的变量。
③:同步监视器:俗称锁,任何一个类的对象都可以来充当锁。
④:要求:多个线程必须要共用同一把锁。

2.2、实例代码
package Thread;
/**
 * @author Muluo
 * @create 2021-01-18 21:21
 */
class window implements Runnable{
    private int ticket = 100;
    // 同步监视器
    Object obj = new Object();

    @Override
    public void run() {
        while(true) {
            // 同步代码块
           synchronized (obj) {
               if (ticket > 0) {
                   System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
                   ticket--;
                   try {
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               } else {
                   break;
               }
           }
        }
    }
}

public class WindowTicket {
    public static void main(String[] args) {
        window window = new window();
        Thread thread1 = new Thread(window);
        thread1.setName("窗口一");
        thread1.start();

        Thread thread2 = new Thread(window);
        thread2.setName("窗口二");
        thread2.start();

        Thread thread3 = new Thread(window);
        thread3.setName("窗口三");
        thread3.start();
    }
}
3、例子:创建个窗口卖票,总票数为100张.使用继承Thread的方式

注意:此方式要注意监视器中的对象 声明为一个静态的,从而确保是一个对象

package Thread;

/**
 * @author Muluo
 * @create 2021-01-18 21:52
 */
class window2 extends Thread{
    private static int ticket = 100;
    // 同步监视器 要声明为静态的,才能保证是一个对象,否则依然会出现不安全的问题
    private static Object obj = new Object();

    @Override
    public void run() {
        while(true) {
            // 同步代码块 this(当前对象也可以) window2.class -> 反射 类也是对象,所以也可以作为同步监视器 是唯一的,只会加载一次
            synchronized (obj) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
                    ticket--;
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }
    }
}
public class WindowTicket2 {
    public static void main(String[] args) {
        window2 window2 = new window2();
        window2 window3 = new window2();
        window2 window4 = new window2();
        window2.start();
        window3.start();
        window4.start();
    }
}
补充:

1、在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
2、在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。(反射)

2.3、同步方法

实现Runnable接口的同步方法

package Thread;

/**
 * 使用同步方法解决实现Runable 接口的线程问题
 *
 * 同步仍然涉及到同步监视器,只是不需要显示的声明
 * 非静态的同步方法,同步监视器是this
 * 静态的同步方法,同步监视器是当前类本身
 * @author Muluo
 * @create 2021-01-19 19:43
 */
class window3 implements Runnable {
    private int ticket = 100;

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

    private synchronized void show() { // 同步监视器就是this
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
            ticket--;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class WindowTicket3 {
    public static void main(String[] args) {
        window window = new window();
        Thread thread1 = new Thread(window);
        thread1.setName("窗口一");
        thread1.start();

        Thread thread2 = new Thread(window);
        thread2.setName("窗口二");
        thread2.start();

        Thread thread3 = new Thread(window);
        thread3.setName("窗口三");
        thread3.start();
    }
}

同步方法 继承Thread类来实现线程安全问题

package Thread;

/**
 * 同步方法 继承Thread类来实现线程安全问题
  @author Muluo
 * @create 2021-01-19 19:53
 */
class window4 extends Thread {
    private static int ticket = 100;


    @Override
    public void run() {
        while (true) {
            show();
        }
    }
    // 变成static synchronized
    private static synchronized void show() { // 同步监视器是Window4
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
            ticket--;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class WindowTicket4 {
    public static void main(String[] args) {
        window2 window2 = new window2();
        window2 window3 = new window2();
        window2 window4 = new window2();
        window2.start();
        window3.start();
        window4.start();
    }
}

同步仍然涉及到同步监视器,只是不需要显
非静态的同步方法,同步监视器是this
静态的同步方法,同步监视器是当前类本身

2.4利用同步锁解决线程问题(Lock___JDK 5.0以后新增)
package Thread;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 解决线程的安全问题,方式三,Lock方式,JDK5.0新增
 * @author Muluo
 * @create 2021-01-19 20:12
 */
class WindowLock implements Runnable{
    private int ticket = 100;

    // ctrl + p


    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                // 调用lock方法
                lock.lock();

                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            } finally {
                // 解锁
                lock.unlock();
            }
        }
    }
}
public class LockTest {
    public static void main(String[] args) {
        WindowLock w1 = new WindowLock();
        Thread thread1 = new Thread(w1);
        thread1.start();

        Thread thread2 = new Thread(w1);
        thread2.start();

        Thread thread3 = new Thread(w1);
        thread3.start();
    }
}

!!!synchronized与Lock的异同
同:二者都可以解决线程安全问题
异:
1.synchronized执行完相应的代码后,会自动释放同步监视器
2.Lock需要手动的启动同步(Lock()),同时结束同步也需要手动去实现(unlock())
优先使用Lock更灵活

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值