同步

1.线程间的相互干扰

笔者上一篇文章讲到使用Runnable来实现多线程可以共享资源,但是这也带来了问题。我们来看上一篇文章的一段代码

public class MyThread implements Runnable {
	private int ticket = 5;

	public void run() {
		while (true) {
			if (ticket > 0) {
				//加入休眠使效果更明显
				try {
					Thread.sleep(4000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//输出正在运行的线程名字以及剩余票数
				System.out.println(Thread.currentThread().getName()+"正在售票" + ticket--);
			}
		}
	}
}




public class ThreadTest {

	public static void main(String[] args) {
		MyThread mt=new MyThread();
		//启动线程
		new Thread(mt).start();
		new Thread(mt).start();
		new Thread(mt).start();
	}

}

当运行这段代码时可能会出现以下情况(这种错误的出现不可预测,笔者也是运行了多次程序才出现):Thread-0正在售票0

Thread-2正在售票-1

按道理来说这两种情况是不应该出现的,那么造成这种错误的原因是什么呢?

这是因为有多个线程对同时对ticket进行了操作。我们来模拟这个过程:

整个线程运行的过程是获取当前ticket的值,进入判断语句若符合条件则休眠4秒,然后输出当前ticket的值,再将ticket减1然后返回。

假设线程1获得了当前Ticket的值1,但是还没有来的及将ticket减1返回。但是此时线程0和线程2已经先后进入判断语句,就在这时ticket减1了,然后线程0输出的就是减1后的结果0了,线程0再减1返回,线程2获得的是再次减1的结果,即-1.

2.内存一致性错误

int count=0

假设有A和B两个线程共享这个变量。A线程对变量进行count++操作,线程B对变量进行输出操作System.out.println(count);

这是线程B输出的结果可能是0。因为线程A对变量的操作对线程B不可见。

解决以上两个错误的方法之一就是使用同步。

3.同步方法

定义同步方法的格式:访问修饰符 synchronized 返回值类型 方法名(数据类型 变量名){

方法体;

}

以上文中的代码为例

public class MyThread implements Runnable {
	private int ticket = 5;

	public void run() {
		while (true) {
			this.sale();
		}
	}
        /*
        *同步方法
        */
	public synchronized void sale(){

		if (ticket > 0) {
			//加入休眠使效果更明显
			try {
				Thread.sleep(4000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//输出正在运行的线程名字以及剩余票数
			System.out.println(Thread.currentThread().getName()+"正在售票" + ticket--);
		}
	
	}
}





public class ThreadTest {

	public static void main(String[] args) {
		MyThread mt=new MyThread();
		//启动线程
		new Thread(mt).start();
		new Thread(mt).start();
		new Thread(mt).start();
	}

}

4.同步代码块

同步代码块定义格式:synchronized(){

需要同步的代码;

}

public class MyThread implements Runnable {
	private int ticket = 5;

	public void run() {
		while (true) {
			//同步代码块
                          synchronized (this) {

				if (ticket > 0) {
					// 加入休眠使效果更明显
					try {
						Thread.sleep(4000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					// 输出正在运行的线程名字以及剩余票数
					System.out.println(Thread.currentThread().getName() + "正在售票" + ticket--);
				}
			}
		}
	}
}



public class ThreadTest {

	public static void main(String[] args) {
		MyThread mt=new MyThread();
		//启动线程
		new Thread(mt).start();
		new Thread(mt).start();
		new Thread(mt).start();
	}

}

注意:1)使用太多的同步容易出现死锁,关于死锁本篇文章不做说明,笔者将另写一篇。

2)原子性动作是指一次性完成的动作,即要么一次执行完所有的动作要么不执行,volatile声明的所有变量都是原子性动作。使用volati可以避免线程互相干扰,减少内存一致性错误。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值