//synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法
//每个对象只有一个锁(lock)与之相关联。
//实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
/*
无论synchronized关键字加在方法上还是对象上,它取得的锁都是锁在了对象上,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
每个对象只有一个锁(lock)与之相关联。
实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。
还有一些技巧可以让我们对共享资源的同步访问更加安全:
定义private 的instance变量+它的 get方法,而不要定义public/protected的instance变量。如果将变量定义为public,对象在外界可以绕过同步方法的控制而直接取得它,并改动它。
如果instance变量是一个对象,如数组或ArrayList什么的,那上述方法仍然不安全,因为当外界对象通过get方法拿到这个instance对象的引用后,又将其指向另一个对象,那么这个private变量也就变了,岂不是很危险。 这个时候就需要将get方法也加上synchronized同步,并且,只返回这个private对象的clone()――这样,调用端得到的就是对象副本的引用了。
*/
/*
Wait时别的线程可以访问锁定对象
调用wait方法的时候必需锁定该对象
Object提供的方法
Sleep时别的线程也不可以访问锁定对象
Thread提供的方法
*/

public 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();
//new Foo().methodBBB();
}
public void run(){
timer.add(Thread.currentThread().getName());
}
}
class Timer{
private int num = 0;
String s = "123";
public void add(String name){
synchronized (s) { // this只当前Timer对象
num ++;
try {Thread.sleep(1);}
catch (InterruptedException e) {}
System.out.println(name+", 你是第"+num+"个使用timer的线程");
}
}
public int readNum() {
return num;
}
}












/////////////////////////////////////////////////////////////////////////////////////////////////////////
//第一种情况
//修饰方法
//这也就是同步方法,那这时synchronized锁定的是哪个对象呢?
//它锁定的是调用这个同步方法对象。
//也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。
//但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。
/////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Public synchronized void methodAAA(){
//……
}
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////





//////////////////////////////////////////////////////////////////////////////////////////////////
//第二种情况
//当有一个明确的对象作为锁时,就可以这样对so这个对象加锁,谁拿到这个锁谁就可以运行它所控制的那段代码
/*
public void method3(SomeObject so){
synchronized(so){
//……
}
}
*/
////////////////////////////////////////////////////////////////////////////////////////////////////












///////////////////////////////////////////////////////////////////////////////////////////////
//第三种情况
//但当没有明确的对象作为锁,只是想让一段代码同步时,
//可以创建一个特殊的instance变量(它得是一个对象)来充当锁:
//注:零长度的byte数组对象创建起来将比任何对象都经济
//查看编译后的字节码:
//生成零长度的byte[]对象只需3条操作码,
//而Object lock = new Object()则需要7行操作码。
/*
class Foo implements Runnable{
private byte[] lock = new byte[0]; // 特殊的instance变量
Public void methodA(){
synchronized(lock) {
//……
}
}
//……
}
*/
///////////////////////////////////////////////////////////////////////////////////////////////////





/////////////////////////////////////////////////////////////////////////////////////////////////
//第四种情况
//将synchronized作用于static 函数,示例代码如下
//代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,
//取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
//------>下面的说法可能有问题!
//如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,
//那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,
//因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
/*
class Foo {
public synchronized static void methodAAA(){ // 同步的static 函数
try {Thread.sleep(1000);}catch (InterruptedException e) {}
System.out.println("methodAAA......");
}
public void methodBBB(){
synchronized(Foo.class){ // class literal(类名称字面常量)
}
System.out.println(Foo.class.getName() + ".class");
}
}
*/
////////////////////////////////////////////////////////////////////////////////////////////////////
补充:
class Foo {
static Foo foo = new Foo();
// 同步的static 函数
public synchronized static void methodAAA(){
System.out.println("methodAAA......");
try {
Thread.sleep(5000);
}catch (InterruptedException e) {
}
}
// 同步Foo.class.....说明:class literal(类名称字面常量)
public void methodBBB(){
synchronized(Foo.class){
System.out.println(Foo.class.getName() + ".class");
try {
Thread.sleep(5000);
}catch (InterruptedException e) {
}
}
}
//普通方法
public void methodCCC(){
System.out.println("generic method ......");
try {
Thread.sleep(5000);
}catch (InterruptedException e) {
}
}
}
class TestFoo{
public static void main(String [] args){
//通过不同的实例调用methodAAA
//new Thread(new A()).start();
//new Thread(new A()).start();
//通过不同的实例调用methodBBB
//new Thread(new B()).start();
//new Thread(new B()).start();
//通过不同的实例调用methodAAA和methodBBB
//new Thread(new A()).start();
//new Thread(new B()).start();
//通过相同的实例调用methodAAA
//new Thread(new AA()).start();
//new Thread(new AA()).start();
//通过相同的实例调用methodBBB
//new Thread(new BB()).start();
//new Thread(new BB()).start();
//通过相同的实例调用methodAAA和methodBBB
//new Thread(new AA()).start();
//new Thread(new BB()).start();
//调用普通方法
//Foo.foo.methodCCC();
}
}
class A implements Runnable {
public void run() {
new Foo().methodAAA();
}
}
class B implements Runnable {
public void run() {
new Foo().methodBBB();
}
}
class AA implements Runnable {
public void run() {
Foo.foo.methodAAA();
}
}
class BB implements Runnable {
public void run() {
Foo.foo.methodBBB();
}
}
本文深入探讨Java中的同步机制,包括synchronized关键字的使用方法及其锁定原理。解释了如何利用同步来确保多线程环境下资源的安全访问,并提供了四种不同场景下的应用示例。

1517

被折叠的 条评论
为什么被折叠?



