[java多线程]多线程同步(一)——synchronized
2014-08-24 14:55
423 查看
一、synchronized修饰方法
synchronized关键字可以用来修饰并保护我们在类中定义的方法,保证在某一时刻某个方法只有一个线程能访问,
对于普通成员方法,synchronized实际上是对这个类的某个对象实例加锁,等效于synchronized(this){...}。
1、一个方法同步无效的例子
线程类定义:
原因自然就是synchronized普通方法实际上是对对象自身的this加锁,三个线程是三个独立的对象,有三个不同的this,锁当然就无效了。
2、修饰普通方法
2.1. 还是用synchronized修饰run()方法
线程类定义:
看到和刚才的线程并没有什么不同,仍然是对run()方法加锁;
测试类定义:
执行结果是一个线程的run()方法执行完毕,下一个线程的run()方法才会执行,
因为三个线程都共用一个myThread对象,加锁的对象是同一个this,自然就能正确同步了。
2.2.
用synchronized修饰其它方法
线程类定义:
我们看到,run()方法调用另外两个同步方法otherMethodOne()和otherMethodTwo(),每个其它同步方法内部将循环打印几次,
测试类定义:
但同步方法执行的时候,不会同时执行别的同步方法的打印语句,
可见,只对synchronized修饰的方法内部实现加锁,同步方法执行完毕之后,锁就被释放,其它线程也就有机会竞争得到这个锁;
但如果测试类如下方式执行线程:
3、修饰静态方法
对于静态成员方法,synchronized实际上是对这个类加锁,等效于synchronized(ClassName.class){...}。
线程类定义:
说明:run()方法可不能static...所以我们将其他方法变成静态方法;
测试类定义:
这里是三个独立的MyThread对象,执行结果可以看到一个静态方法执行完毕,另外的静态方法才会执行,
没有用下面的形式,之前就讨论过了,还用得着静态方法么?费那事干嘛……
二、synchronized修饰成员对象
1、修饰基本数据类型,行不行?
看下面的线程类定义:
显然在编译时就会提示不行了,
对于这种情况我们可以用基本数据类型对应的包装类,比如这里把变量num换成Integer类型的就可以了,编译就不报错了,
但是真的就可以按照我们预想的执行么?我们接着往下看...
2、修饰普通成员对象
修饰普通成员对象,synchronized对这个成员对象加锁。
2.1. 我们用Integer对象来试试
线程类定义:
那换成下面这样的测试类定义呢?
调用类的构造方法时,直接赋值成0不行,那我们换成下面这样的测试类定义呢?
有网上的朋友给出这样的解释,转帖过来:
对于
Integer j = new Integer(10);
j++等同于j = new Integer(j + 1);
用下面的代码可以证明上面的结论:
2.2. 我们用自定义的bean来试试
自定义bean如下:
线程类定义:
测试类定义:
3、修饰静态对象
修饰静态成员对象,synchronized对这个静态成员对象加锁,
因为java的同一个类的多个对象共用同一个静态成员对象,所以能保证多线程同步的正常工作。
还是刚才的bean定义:
synchronized关键字可以用来修饰并保护我们在类中定义的方法,保证在某一时刻某个方法只有一个线程能访问,
对于普通成员方法,synchronized实际上是对这个类的某个对象实例加锁,等效于synchronized(this){...}。
1、一个方法同步无效的例子
线程类定义:
/** * 这里我们在run()方法中加入了synchronized关键字,希望能对run方法进行互斥访问,但结果并不如我们希望那样, * 这是因为这里synchronized锁住的是this对象,即当前运行线程对象本身。 * 代码中创建了3个线程,而每个线程都持有this对象的对象锁,这不能实现线程的同步。 */ public class MyThread implements Runnable { private int threadId; public MyThread(int id) { this.threadId = id; } @Override public synchronized void run() { String threadName = Thread.currentThread().getName(); for (int i = 0; i < 5; i++) { System.out.println("threadName: " + threadName + ", Thread ID: " + this.threadId + ", i: " + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }测试类定义:
public class Test { public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 3; ++i) { new Thread(new MyThread(i)).start(); Thread.sleep(1); } } }执行结果可以看到,三个线程还是在并发的执行,并没有因为run()方法加了synchronized修饰就同步了,
原因自然就是synchronized普通方法实际上是对对象自身的this加锁,三个线程是三个独立的对象,有三个不同的this,锁当然就无效了。
2、修饰普通方法
2.1. 还是用synchronized修饰run()方法
线程类定义:
public class MyThread implements Runnable { @Override public synchronized void run() { String threadName = Thread.currentThread().getName(); for (int i = 0; i < 5; i++) { System.out.println("threadName: " + threadName + ", i: " + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
看到和刚才的线程并没有什么不同,仍然是对run()方法加锁;
测试类定义:
public class Test { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); for (int i = 0; i < 3; ++i) { new Thread(myThread).start(); Thread.sleep(1); } } }
执行结果是一个线程的run()方法执行完毕,下一个线程的run()方法才会执行,
因为三个线程都共用一个myThread对象,加锁的对象是同一个this,自然就能正确同步了。
2.2.
用synchronized修饰其它方法
线程类定义:
public class MyThread implements Runnable { @Override public void run() { otherMethodOne(); otherMethodTwo(); } private synchronized void otherMethodOne() { String threadName = Thread.currentThread().getName(); for (int i = 0; i < 5; i++) { System.out.println("threadName: " + threadName + ", otherMethodOne, i: " + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } private synchronized void otherMethodTwo() { String threadName = Thread.currentThread().getName(); for (int i = 0; i < 5; i++) { System.out.println("threadName: " + threadName + ", otherMethodTwo, i: " + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
我们看到,run()方法调用另外两个同步方法otherMethodOne()和otherMethodTwo(),每个其它同步方法内部将循环打印几次,
测试类定义:
public class Test { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); for (int i = 0; i < 3; ++i) { new Thread(myThread).start(); Thread.sleep(1); } } }执行结果显示,一个线程的run()方法一个同步方法执行完毕,不一定紧接着执行第二个同步方法,而很有可能执行其他线程的某个同步方法,
但同步方法执行的时候,不会同时执行别的同步方法的打印语句,
可见,只对synchronized修饰的方法内部实现加锁,同步方法执行完毕之后,锁就被释放,其它线程也就有机会竞争得到这个锁;
但如果测试类如下方式执行线程:
public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 3; ++i) { new Thread(new MyThread()).start(); Thread.sleep(1); } }可想而知,同步又会失效……
3、修饰静态方法
对于静态成员方法,synchronized实际上是对这个类加锁,等效于synchronized(ClassName.class){...}。
线程类定义:
public class MyThread implements Runnable { private int threadId; public MyThread(int id) { this.threadId = id; } @Override public void run() { otherMethodOne(this.threadId); otherMethodTwo(this.threadId); } private static synchronized void otherMethodOne(int threadId) { String threadName = Thread.currentThread().getName(); for (int i = 0; i < 5; i++) { System.out.println("threadName: " + threadName + ", Thread ID: " + threadId + ", otherMethodOne, i: " + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } private static synchronized void otherMethodTwo(int threadId) { String threadName = Thread.currentThread().getName(); for (int i = 0; i < 5; i++) { System.out.println("threadName: " + threadName + ", Thread ID: " + threadId + ", otherMethodTwo, i: " + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
说明:run()方法可不能static...所以我们将其他方法变成静态方法;
测试类定义:
public class Test { public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 3; ++i) { new Thread(new MyThread(i)).start(); Thread.sleep(1); } } }说明:
这里是三个独立的MyThread对象,执行结果可以看到一个静态方法执行完毕,另外的静态方法才会执行,
没有用下面的形式,之前就讨论过了,还用得着静态方法么?费那事干嘛……
MyThread myThread = new MyThread(...); for (int i = 0; i < 3; ++i) { new Thread(myThread).start(); Thread.sleep(1); }
二、synchronized修饰成员对象
1、修饰基本数据类型,行不行?
看下面的线程类定义:
显然在编译时就会提示不行了,
对于这种情况我们可以用基本数据类型对应的包装类,比如这里把变量num换成Integer类型的就可以了,编译就不报错了,
但是真的就可以按照我们预想的执行么?我们接着往下看...
2、修饰普通成员对象
修饰普通成员对象,synchronized对这个成员对象加锁。
2.1. 我们用Integer对象来试试
线程类定义:
public class MyThread implements Runnable { private Integer num; // int is not work... public MyThread(Integer num) { this.num = num; } @Override public void run() { String threadName = Thread.currentThread().getName(); for (int i = 0; i < 5; i++) { synchronized (num) { System.out.println("threadName: " + threadName + ", num: " + num++); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }测试类定义:
public class Test { public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(0); for (int i = 0; i < 3; ++i) { new Thread(myThread).start(); Thread.sleep(1); } } }从执行结果可以看到num变量是原子的方式增加的,num从0增长到14,没有发生混乱;
那换成下面这样的测试类定义呢?
public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 3; ++i) { new Thread(new MyThread(0)).start(); Thread.sleep(1); } }执行结果每个线程中num都从0开始自增,显然这个不是我们想看到的,
调用类的构造方法时,直接赋值成0不行,那我们换成下面这样的测试类定义呢?
public static void main(String[] args) throws InterruptedException { Integer num = new Integer(0); for (int i = 0; i < 3; ++i) { new Thread(new MyThread(num)).start(); Thread.sleep(1); } }还是一样,执行结果每个线程中num都从0开始自增,为什么不行呢?
有网上的朋友给出这样的解释,转帖过来:
对于
Integer j = new Integer(10);
j++等同于j = new Integer(j + 1);
用下面的代码可以证明上面的结论:
Integer j = new Integer(10); // Different hash code will be printed. System.out.println(System.identityHashCode(j)); j++; System.out.println(System.identityHashCode(j)); j = new Integer(j + 1); System.out.println(System.identityHashCode(j));
2.2. 我们用自定义的bean来试试
自定义bean如下:
public class MyBean { int num; public MyBean(int num) { this.num = num; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } }
线程类定义:
public class MyThread implements Runnable { private MyBean bean; public MyThread(MyBean bean) { this.bean = bean; } @Override public void run() { String threadName = Thread.currentThread().getName(); for (int i = 0; i < 5; i++) { synchronized (bean) { int num = bean.getNum(); System.out.println("threadName: " + threadName + ", num: " + num); bean.setNum(num + 1); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
测试类定义:
public class Test { public static void main(String[] args) throws InterruptedException { MyBean bean = new MyBean(0); for (int i = 0; i < 3; ++i) { new Thread(new MyThread(bean)).start(); Thread.sleep(1); } } }执行结果可以看出,bean中的num从0增长到14,我们想要的共享和同步的效果就是这样。
3、修饰静态对象
修饰静态成员对象,synchronized对这个静态成员对象加锁,
因为java的同一个类的多个对象共用同一个静态成员对象,所以能保证多线程同步的正常工作。
还是刚才的bean定义:
public class MyBean { int num; public MyBean(int num) { this.num = num; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } }线程类定义:
public class MyThread implements Runnable { private static MyBean bean = new MyBean(0); @Override public void run() { String threadName = Thread.currentThread().getName(); for (int i = 0; i < 5; i++) { synchronized (bean) { int num = bean.getNum(); System.out.println("threadName: " + threadName + ", num: " + num); bean.setNum(num + 1); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }测试类定义:
public class Test {从执行结果可以看到bean中的num从0增长到14。,
public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 3; ++i) { new Thread(new MyThread()).start(); Thread.sleep(1); } }
}
相关文章推荐
- java多线程-多线程的安全问题-多线程同步代码块-锁Synchronized
- Java中级----多线程同步基本思想,java多线程设计wait、notify、notifyall、synchronized的使用机制(转)
- Java 多线程:synchronized 多线程同步关键字
- java多线程-多线程同步代码块-银行存款实例-锁synchronized
- Java 多线程:synchronized 多线程同步关键字
- Java【多线程知识总结(7)】多线程同步问题-关于synchronized代码块和synchronized方法的应用
- Java【多线程知识总结(7)】多线程同步问题-关于synchronized代码块和synchronized方法的应用
- Java多线程Synchronized心得
- java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解
- java多线程设计wait/notify机制 (synchronized与对象锁)
- java多线程设计wait、notify、notifyall、synchronized的使用机制
- Java 多线程同步 锁机制与synchronized
- Java的多线程编程模型4--synchronized
- java多线程synchronized注意的地方
- 初学Java多线程:使用Synchronized关键字同步类方法
- java多线程总结四:volatile、synchronized示例
- java多线程总结四:volatile、synchronized示例
- Java 多线程同步问题的探究(四、协作,互斥下的协作——Java多线程协作(wait、notify、notifyAll))
- Java多线程Synchronized的注意细节
- Java中的多线程同步---Synchronized