关于多线程的笔记2

1.背景
创建个窗口卖票,总票数为100张,(应该会有4段代码演示)

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

synchronized(同步监视器){
	//需要被同步的代码
}

说明:
1.被同步的代码也就是操作共享数据的代码
2.共享数据:多个线程共同操作的数据,需要使用同步机制将操作共享数据的代码包起来,不能包多了,也不能包少了。
3.同步监视器:俗称锁。任何一个类的对象都可以是锁
要求就是:多个线程必须要共用同一把锁。

补充:
1.在实现Runnable接口创建多线程的方式中,可以用this充当同步监视器。
2.在继承Thread类创建多线程的方式中,慎用this充当同步监视器,一般用当前类充当。

使用同步代码块解决继承Thread类的方式的线程安全问题

class Window extends Thread{
    private static int ticket = 100;

//    static Cat cat = new Cat();
    @Override
    public void run() {
        while(true){
//            synchronized(cat)
            synchronized(Window.class){//小伙伴们也可以用this试试,看看效果怎么样。
                if (ticket > 0){
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(getName() + ": 卖票,票号为 :" + ticket);
                    ticket--;
                }else{
                    break;
                }

            }
        }


    }
}


public class WindowTest {
    public static void main(String[] args) {
        Window w1 = new Window();
        Window w2 = new Window();
        Window w3 = new Window();
        w1.setName("窗口一");
        w2.setName("窗口二");
        w3.setName("窗口三");

//        w1.setPriority(Thread.MAX_PRIORITY);
        w2.setPriority(Thread.MAX_PRIORITY);
//        w3.setPriority(Thread.MIN_PRIORITY);
        w1.start();
        w2.start();
        w3.start();
    }
}

使用同步代码块解决实现Runnable接口的方式的线程安全问题

class Window1 implements Runnable{

    private int ticket = 100;
    //Object obj = new Object();
//    Dog dog = new Dog();
    @Override
    public void run() {
        while (true){
            synchronized(Window1.class){//类也是对象
                //synchronized(this)错误的,对象不唯一,也就代表着锁不唯一,也就是说会存在线程安全问题。这个说法有待商榷,本人跑的时候,没差别。欢迎留言讨论。。。
                if (ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket);
                    ticket--;
                }else {
                    break;
                }

            }
        }
    }
}


public class WindowTest1 {
    public static void main(String[] args) {
        Window1 window1 = new Window1();
        Thread t1 = new Thread(window1);

        Thread t2 = new Thread(window1);

        Thread t3 = new Thread(window1);

        t1.setName("窗口1***");
        t2.setName("窗口2---");
        t3.setName("窗口3!!!");
        t1.start();
        t2.start();
        t3.start();
    }
}

方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。
关于同步方法的总结
1.同步方法仍然涉及到同步监视器,只是不需要显式的声明。
2.非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
使用同步方法来处理继承Thread类的线程安全问题

class Window extends Thread{
	private static int ticket = 100;
	@Override
	public void run(){
		while(true){
			show();
		}
	}
	public static synchonized void show(){//同步监视器:Window.class
		//private synchonized void show(){//同步监视器:t1,t2,t3。此种解决方式是错误的
		if(ticket > 0){
			 try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ": 卖票,票号为 :" + ticket);
            ticket--;
		}
	}
}

public class WindowTest{
	private static void main(String[] args){
		Window w1 = new Window();
		Window w2 = new Window();
		Window w3 = new Window();
		
		w1.start();
		w2.start();
	}
}

使用同步方法解决实现Runnable接口的线程安全问题

class Window2 implements Runnable{

    private int ticket = 100;
    @Override
    public void run() {
        while (true){
            show();
        }
    }

    private synchronized void show(){//同步监视器:this

        if (ticket > 0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket);
            ticket--;
        }
    }
}


public class WindowTest2 {
    public static void main(String[] args) {
        Window2 window2 = new Window2();
        Thread t1 = new Thread(window2);

        Thread t2 = new Thread(window2);

        Thread t3 = new Thread(window2);

        t1.setName("窗口1***");
        t2.setName("窗口2---");
        t3.setName("窗口3!!!");
        t1.start();
        t2.start();
        t3.start();
    }
}

方式三:Lock锁
优先使用顺序
lock->同步代码块(已经进入了方法体,分配了相应资源)->同步方法(方法体外)

class Window implements Runnable{
	private int ticket = 100;
	//实例化lock
	private ReentrantLock lock = new ReentrantLock();
	@Override
	public void run(){
		while(true){
		try(){
			if(ticket > 0){
				//调用lock
				lock.lock();
				try {
					try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
                    ticket--;
				}
			else {
				break;
				}
			}
		}finally{
			
		}
		}
	}
}

public class LockTest {
    public static void main(String[] args) {
        Windows window = new Windows();

        Thread t1 = new Thread(window);
        Thread t2 = new Thread(window);
        Thread t3 = new Thread(window);

        t1.setName("OOO");
        t2.setName("TTT");
        t3.setName("SSS");

        t1.start();
        t2.start();
        t3.start();

    }
}

面试题: 如何解决线程安全问题?有几种方式?
使用同步机制,有三种。

**面试题:**synchronized 与 lock的异同?
同:二者都可以解决线程安全问题。
异:sychronized机制在执行完相应的同步代码之后,自动的释放同步监视器,而lock则需要手动启动同步(lock()),同时结束同步也需要手动的实现(unlock())

使用同步机制将单例模式中的懒汉式改写为线程安全的

public class BankTest{	
}
class Bank{
	//懒汉式
	private Bank(){
		
	}
	private static Bank instance = null;
	public static Bank getInstance(){
		if(instance == null){
			synchonized(Bank.class){
				if(instance == null){
					instance = new Bank();
				}
			}
		}
		return instance;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值