Java 中的线程同步问题:

  1. 线程同步:

      对于访问同一份资源的多个线程之间, 来进行协调的这个东西.

  2. 同步方法:

     当某个对象调用了同步方法时, 该对象上的其它同步方法必须等待该同步方法执行完毕后, 才能被执行.

  3. 同步块:

       通常将共享资源的操作放置在 synchronized 定义的区域内, 这样当其它线程也获取到这个锁时, 必须等待锁被释放, 才能进入该区域.

Demo_1:

class Timer {
	private static int num = 0;
	public void add(String name) {
		num++;
		try {
			Thread.sleep(100); // 即使不写 Thread.sleep(100), 这个结果可能是对的,也有可能是错的, 不确定. 中间还是有可能被打断.
		} catch (InterruptedException e) {
		}
		System.out.println(name+", 你是第 "+num+"个使用timer的线程");
	}
}
class TestSync implements Runnable {
	Timer timer = new Timer();
	public static void main(String[] args) {
		TestSync test = new TestSync();
		Thread t1 = new Thread(test);
		Thread t2 = new Thread(test);
		t1.setName("t1");
		t2.setName("t2");
		t1.start();
		t2.start();
	}
	@Override
	public void run() {
		timer.add(Thread.currentThread().getName());
	}
}

 会出现的运行结果:

t1, 你是第 2个使用timer的线程
t2, 你是第 2个使用timer的线程

原因:这个线程在执行 add()方法的时候, 被另外一个线程给打断了.

解决办法:

   num++;
   try {Thread.sleep(100); 
    } catch (InterruptedException e) {}

  上面这几句话, 应该作为一个原子性的输出, 你不应该在中途打断.

  第一种解决办法:采用同步块, 如 Demo_2

Demo_2:

class Timer {
	private static int num = 0;
	public void add(String name) {
		synchronized(this){ // 同步块
			num++;
			try {Thread.sleep(100); 
			} catch (InterruptedException e) {}
			System.out.println(name+", 你是第 "+num+"个使用timer的线程");
		}
	}
}
class TestSync implements Runnable {
	Timer timer = new Timer();
	public static void main(String[] args) {
		TestSync test = new TestSync();
		Thread t1 = new Thread(test);
		Thread t2 = new Thread(test);
		t1.setName("t1");
		t2.setName("t2");
		t1.start();
		t2.start();
	}
	@Override
	public void run() {
		timer.add(Thread.currentThread().getName());
	}
}
// 运行结果如下:
// t1, 你是第 1个使用timer的线程
// t2, 你是第 2个使用timer的线程

【注】:既然锁定了当前对象了, 那么这个 num 也就锁定了, 它里面的成员变量当然也锁定(互斥锁).

【注】:锁定当前对象:这执行后面的大括号里面的语句的过程中, 一个线程的执行过程中, 不会被另外一个线程锁打断.

      一旦某个线程已经进入到锁定的区域当中, 那么你放心, 不可能有另外一个线程也在里面(锁的机制).

  第二种解决办法:采用同步块, 如 Demo_3

Demo_3:

class Timer {
	private static int num = 0;
	public synchronized void add(String name) { //同步方法
			num++;
			try {Thread.sleep(100); 
			} catch (InterruptedException e) {}
			System.out.println(name+", 你是第 "+num+"个使用timer的线程");
	}
}
class TestSync implements Runnable {
	Timer timer = new Timer();
	public static void main(String[] args) {
		TestSync test = new TestSync();
		Thread t1 = new Thread(test);
		Thread t2 = new Thread(test);
		t1.setName("t1");
		t2.setName("t2");
		t1.start();
		t2.start();
	}
	@Override
	public void run() {
		timer.add(Thread.currentThread().getName());
	}
}
// 运行结果如下:
// t1, 你是第 1个使用timer的线程
// t2, 你是第 2个使用timer的线程

 分析 Demo_3 的执行过程:

  t1 开始执行, 调用 add() 方法, num++, 然后 t1 睡着了, 睡着了也没关系, t1 睡着也抱着那把锁.

  别人(t2)也进不来, 你必须等它执行完了, 你才可以继续执行.

  睡着了, 也不放开那把锁, 你也没办法.

  4. 线程同步总结:

    4.1. 在 Java 语言中, 引入了对象互斥锁的概念, 保证共享数据操作的完整性. 每个对象都对应于一个可称为"互斥锁"的标记,

       这个标记保证在任一时刻, 只有一个线程访问该对象.

    4.2. 关键字 synchronized 与对象的互斥锁联系. 当某个对象用 synchronized 来修饰时, 表明该对象在任一时刻只能由一个线程访问. 

posted on 2017-05-09 20:24  牧羊人的世界  阅读(143)  评论(0编辑  收藏  举报