JAVA学习——多线程同步

上一篇对多线程的基本用法进行了简单的小结,这一篇着重说一下多线程同步问题


使用多线程,由于多线程并发执行的特点,所以大大的提高了程序的运行效率,但是,当多个线程去访问同一个资源时,也会出现一些安全问题

就拿我们之前所说过的售票案例。

/**
 * 模拟窗口售票
 * 
 * @author Shawn·Zhang
 */

public class Example01 {

	public static void main(String[] args) {
		TicketWindow tw = new TicketWindow();
		new Thread(tw, "售票口一").start();
		new Thread(tw, "售票口二").start();
		new Thread(tw, "售票口三").start();
		new Thread(tw, "售票口四").start();
	}
}

class TicketWindow implements Runnable {
	private int tickets = 10;

	public void run() {
		while (tickets > 0) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "正在发售第"
					+ tickets-- + "张票");
		}
	}
}


测试结果:


在售票程序while循环中添加了sleep()方法,,这样也就是模拟了售票过程中,线程的延迟。

由于有延迟,当票号减为1时,假设线程1此时出售1号票,对票号进行判断之后,进入while循环,在售票之前通过sleep()方法让线程休眠,这时线程二会进行售票,由于此时此时票号仍为1,因此线程2也会进入循环,同理,四个线程都会进入循环,休眠结束后,四个线程都会卖票,这样就相当于将票号减了四次,结果中也就出现了0、-1、-2这样的现象


所以为了线程的安全问题,我们引入同步代码块以及同步方法


同步代码块

synchronized(lock){

        操作共享资源代码块

}

利用java的同步机制,可以保证在多个线程使用同一个资源时,用于处理共享资源的代码任何时候都只有一个线程进行访问

优化后的代码:

/**
 * 模拟窗口售票
 * @author Shawn·Zhang
 */

public class Example01 {

	public static void main(String[] args) {
		TicketWindow tw = new TicketWindow();
		new Thread(tw,"售票口一").start();
		new Thread(tw,"售票口二").start();
		new Thread(tw,"售票口三").start();
		new Thread(tw,"售票口四").start();
	}
}

class TicketWindow implements Runnable {
	private int tickets = 10;
	Object lock = new Object();

	@Override
	public void run() {
		while (true) {
			synchronized (lock) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if(tickets>0){
					System.out.println(Thread.currentThread().getName() + "正在发售第"
							+ tickets-- + "张票");
				}else{
					break;
				}
			}
		}
	}
}
测试结果:

从测试结果可以看出,没有出现不正确的票。这是因为售票的代码实现了同步,之前出现的线程安全问题得以解决。


同步方法

与同步代码块同理,作用相同,只是书写格式有区别,在这里也写出来,以供学习

synchronized  返回值类型  方法名(){}

以售票案例为例,将上述的代码修改后,如下:

/**
 * 模拟窗口售票
 * @author Shawn·Zhang
 */

public class Example01 {

	public static void main(String[] args) {
		TicketWindow tw = new TicketWindow();
		new Thread(tw,"售票口一").start();
		new Thread(tw,"售票口二").start();
		new Thread(tw,"售票口三").start();
		new Thread(tw,"售票口四").start();
	}
}

class TicketWindow implements Runnable {
	private int tickets = 10;

	@Override
	public void run() {
		while (true) {
			saleTickets();//调用售票方法
			if(tickets<=0){
				break;
			}
		}
	}
	
	private synchronized void saleTickets(){
		if(tickets>0){
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "正在发售第"
					+ tickets-- + "张票");
		}
	}
}

结果可以参考同步代码块的测试结果。


死锁

在同步问题上,需要注意死锁问题,就是两个线程在运行的过程中,都在等待对方的锁,这样就会使得程序停滞

在开发中,要避免死锁的情况

public class Example06 {

	public static void main(String[] args) {
		Thread t1 = new Thread(new DeadLock(true));
		Thread t2 = new Thread(new DeadLock(false));
		t1.start();
		t2.start();
	}

}

class DeadLock implements Runnable{
	private boolean flag;//定义boolean类型的变量flag
	static Object locka = new Object();//定义锁对象locka
	static Object lockb = new Object();//定义锁对象lockb
	public DeadLock(boolean flag) {//有参构造方法
		this.flag = flag;
	}
	@Override
	public void run() {
		if(flag){
			while(true){
				synchronized(locka){//锁对象上的同步代码块
					System.out.println("if locka");
					synchronized (lockb) {//锁对象上的同步代码块
						System.out.println("if lockb");
					}
				}
			}
		}else{
			while(true){
				synchronized(lockb){//锁对象上的同步代码块
					System.out.println("else lockb");
					synchronized (locka) {//锁对象上的同步代码块
						System.out.println("else locka");
					}
				}
			}
		}
	}
}
测试结果会出现 死锁现象

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值