synchronized

synchronized

前言

相信大家都听说过线程安全问题,在学习操作系统的时候有一个知识点是临界资源,简单的说就是一次只能让一个进程操作的资源,但是我们在使用多线程的时候是并发操作的,并不能控制同时只对一个资源的访问和修改,想要控制那么有几种操作,今天我们就来讲讲第一种方法:线程同步块或者线程同步方法(synchronized)

实例

  1. 下面举一个例子说明synchronized关键字的使用

线程同步方法

public class Sychor {
	public void insert(Thread thread) {
		for (int i = 0; i < 10; i++) {
			System.out.println(thread.getName() + "输出:  " + i);
		}

	}

	public static void main(String[] args) {
		final Sychor sychor = new Sychor();

		Thread t1 = new Thread() {
			public void run() {
				sychor.insert(Thread.currentThread());
			};
		};

		Thread t2 = new Thread() {
			public void run() {
				sychor.insert(Thread.currentThread());
			};
		};

		t1.start();
		t2.start();
	}
}

其中输出结果为下图

运行结果

从上面的结果可以看出这里的两个线程是同时执行insert()方法的,下面我们在原有的代码上添加synchronized关键字看看效果如何,代码如下:

public class Sychor {
	public synchronized void insert(Thread thread) {
		for (int i = 0; i < 10; i++) {
			System.out.println(thread.getName() + "输出:  " + i);
		}

	}

	public static void main(String[] args) {
		final Sychor sychor = new Sychor();

		Thread t1 = new Thread() {
			public void run() {
				sychor.insert(Thread.currentThread());
			};
		};

		Thread t2 = new Thread() {
			public void run() {
				sychor.insert(Thread.currentThread());
			};
		};

		t1.start();
		t2.start();
	}
}

上面程序的运行结果我就不列出来,自己可以试试,总之就是加上了synchronized关键字使得线程是一个一个的执行的,只有先执行完一个线程才能执行了另外一个线程。

线程同步块

当然上面的我们使用的是线程同步方法,我们可以使用线程同步块,这两个相比线程同步块更加灵活,只需要将需要同步的代码放在同步块中即可,代码如下;

public class Sychor {
	public void insert(Thread thread) {
		synchronized (this) {
			for (int i = 0; i < 10; i++) {
				System.out.println(thread.getName() + "输出:  " + i);
			}
            
		}
		

	}

	public static void main(String[] args) {
		final Sychor sychor = new Sychor();

		Thread t1 = new Thread() {
			public void run() {
				sychor.insert(Thread.currentThread());
			};
		};

		Thread t2 = new Thread() {
			public void run() {
				sychor.insert(Thread.currentThread());
			};
		};

		t1.start();
		t2.start();
	}
}

从上面的代码中可以看出这种方式更加灵活,只需要将需要同步的代码方法在同步块中,不需要同步的代码放在外面

详细原因

  1. 我们知道每一个对象都有一把锁,当我们使用线程同步方法或者线程同步块的时候实际上获得是对象的唯一的一把锁,当一个线程获得了这唯一的锁,那么其他的线程只能拒之门外了,注意这里我们说是一个对象,也就是说是同一个对象,如果是不同的对象,那么就不起作用了,因为不同对象有不同的对象锁,比如我们将上面的程序改成如下:

public class Sychor {
	public void insert(Thread thread) {
		synchronized (this) {
			for (int i = 0; i < 10; i++) {
				System.out.println(thread.getName() + "输出:  " + i);
			}
		}

	}

	public static void main(String[] args) {

		//第一个线程
		Thread t1 = new Thread() {
			public void run() {
				Sychor sychor = new Sychor();   //在run() 方法中创建一个对象
				sychor.insert(Thread.currentThread());
			};
		};

		//第二个线程
		Thread t2 = new Thread() {
			public void run() {
				Sychor sychor = new Sychor();  //创建另外的一个对象
				sychor.insert(Thread.currentThread());
			};
		};

		t1.start();
		t2.start();
	}
}

从上面的结果可知,此时线程同步块根本不起作用,因为他们调用的是不同对象的insert方法,获得锁是不一样的

  1. 上面我们已经说过一个对象有一把锁,线程同步方法和线程同步块实际获得的是对象的锁,因此线程同步块的括号中填入的是this,我们都知道this在一个类中的含义
  1. 一个类也有唯一的一把锁,我们前面说的是使用对象调用成员方法,现在如果我们要调用类中的静态方法,那么我们可以使用线程同步方法或者同步块获得类中的唯一一把锁,那么对于多个线程同时调用同一个类中的静态方法就可以实现控制了,代码如下:
public class Sychor {
	// 静态方法
	public static synchronized void insert(Thread thread)  
	{
		for(int i=0;i<10;i++)
		{
			System.out.println(thread.getName()+"输出     "+i);
		}
	}
	public static void main(String[] args) {

		//第一个线程
		Thread t1 = new Thread() {
			public void run() {
				Sychor.insert(Thread.currentThread());  //直接使用类调用静态方法
			};
		};

		//第二个线程
		Thread t2 = new Thread() {
			public void run() {
				Sychor.insert(Thread.currentThread());   //直接使用类调用静态方法
			};
		};

		t1.start();
		t2.start();
	}
}

注意

  1. 要想实现线程安全和同步控制,如果执行的是非static同步方法或者其中的同步块,那么一定要使用同一个对象,如果调用的是static同步方法或者其中的同步块那么一定要使用同一个类去调用
  1. 如果一个线程访问的是static同步方法,而另外一个线程访问的是非static的同步方法,此时这两个是不会发生冲突的,因为一个是类的锁,一个是对象的锁
  1. 如果使用线程同步块,那么同步块中的代码是控制访问的,但是外面的代码是所有线程都可以访问的
  1. 当一个正在执行同步代码块的线程出现了异常,那么jvm会自动释放当前线程所占用的锁,因此不会出现由于异常导致死锁的现象

参考文章

http://www.cnblogs.com/dolphin0520/p/3923737.html

posted @ 2017-06-21 08:02  爱撒谎的男孩  阅读(685)  评论(0编辑  收藏  举报