synchronized关键字的作用是保证多线程操作的安全
简单说:多个线程公用一把锁时,就要一个一个来执行
1.不使用synchronized
class Fun{
public void run(){
for (int i = 0; i < 10; ++i)
{
System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
}
}
}
class MyThread extends Thread{
Fun fun;
public MyThread(Fun fun){
this.fun = fun;
}
@Override
public void run() {
fun.run();
}
}
测试运行
Fun fun = new Fun();
MyThread thread1 = new MyThread(fun);
MyThread thread2 = new MyThread(fun);
thread1.start();
thread2.start();
结果:
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-2
11-13 15:43:53.238 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-3
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-2
11-13 15:43:53.238 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-3
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-2
11-13 15:43:53.238 791-918/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-2
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-3
11-13 15:43:53.239 791-926/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-3
从中可以看出,到底是哪个线程先运行时没有章法的,也就是不同步,哪个线程抢到了运行权哪个线程就运行.
如果使用了synchronized呢?
2.synchronized修饰普通方法
只需稍微修改下:
class Fun{
public synchronized void run(){
for (int i = 0; i < 10; ++i)
{
System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
}
}
}
结果:
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-2
11-13 15:45:26.288 3848-3881/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-2
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-3
11-13 15:45:26.288 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-3
11-13 15:45:26.289 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-3
11-13 15:45:26.289 3848-3882/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-3
从中可以看出,thread2执行完fun.run()方法之后,thread3才会执行,这说明thread2在执行的时候获取了锁,直到执行完之后才把锁释放,这就保证了线程安全
但是,如果我定义两个Fun对象呢,比如这样
Fun fun1 = new Fun();
Fun fun2 = new Fun();
MyThread thread1 = new MyThread(fun1);
MyThread thread2 = new MyThread(fun2);
thread1.start();
thread2.start();
结果如下:
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 0____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-2
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 1____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 2____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 3____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 4____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 5____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 6____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 7____Thread-3
11-13 15:53:56.401 11201-11233/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-2
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 8____Thread-3
11-13 15:53:56.401 11201-11234/com.dgtech.sss.synchronizeddemo I/System.out: Hello: 9____Thread-3
结果说明,线程又不同步了
为什么呢?
因为synchronized修饰的普通方法的的锁是这个类的对象,在示例中我们定义了两个对象,也就是每个thread用的锁不是一个,每个thread都有自己的锁,这跟上面例子不同的地方就是,上面例子只有一把锁,这个有两把锁
接着往下看:
3.synchronized修饰static方法
class Fun{
public static synchronized void run(){
for (int i = 0; i < 10; ++i)
{
System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
}
}
}
class MyThread extends Thread{
Fun fun;
public MyThread(Fun fun){
this.fun = fun;
}
@Override
public void run() {
Fun.run();
}
}
调用情况:
Fun fun1 = new Fun();
Fun fun2 = new Fun();
MyThread thread1 = new MyThread(fun1);
MyThread thread2 = new MyThread(fun2);
thread1.start();
thread2.start();
结果:
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 0____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 1____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 2____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 3____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 4____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 5____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 6____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 7____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 8____Thread-2
11-13 16:05:00.805 19091-19106/? I/System.out: Hello: 9____Thread-2
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 0____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 1____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 2____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 3____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 4____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 5____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 6____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 7____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 8____Thread-3
11-13 16:05:00.805 19091-19107/? I/System.out: Hello: 9____Thread-3
结果是,又同步了.
为什么呢?
因为静态方法属于这个类,synchronized修饰的静态方法的锁是这个类,因为class只有一个,所以锁只有一把,因此就会同步.
既然synchronized修饰的静态方法的锁是这个class,也就是说当这个锁被占有的时候,这个类中的其他需要这个class当锁的方法一样得等到锁才能执行,下面看下例子来说明:
class Fun{
public static synchronized void run(){
for (int i = 0; i < 10; ++i)
{
System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
}
}
public static synchronized void run2(){
for (int i = 0; i < 10; ++i)
{
System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
}
}
}
class MyThread extends Thread{
Fun fun;
public MyThread(Fun fun){
this.fun = fun;
}
@Override
public void run() {
Fun.run();
}
}
class MyThread2 extends Thread{
Fun fun;
public MyThread2(Fun fun){
this.fun = fun;
}
@Override
public void run() {
Fun.run2();
}
}
执行情况:
Fun fun1 = new Fun();
Fun fun2 = new Fun();
MyThread thread1 = new MyThread(fun1);
MyThread2 thread2 = new MyThread2(fun2);
thread1.start();
thread2.start();
结果:
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 0____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 1____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 2____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 3____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 4____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 5____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 6____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 7____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 8____Thread-2
11-13 16:13:32.778 24600-24618/? I/System.out: Hello: 9____Thread-2
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 0____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 1____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 2____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 3____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 4____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 5____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 6____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 7____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 8____Thread-3
11-13 16:13:32.779 24600-24619/? I/System.out: Hello: 9____Thread-3
这两个thread运行的静态方法都需要这个Fun.class当作锁,因此执行是一个接着一个执行的
下面对这个例子稍微修改下
class Fun{
public static synchronized void run(){
for (int i = 0; i < 10; ++i)
{
System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
}
}
public synchronized void run2(){
for (int i = 0; i < 10; ++i)
{
System.out.println("Hello: " + i+"____"+Thread.currentThread().getName());
}
}
}
class MyThread extends Thread{
Fun fun;
public MyThread(Fun fun){
this.fun = fun;
}
@Override
public void run() {
Fun.run();
}
}
class MyThread2 extends Thread{
Fun fun;
public MyThread2(Fun fun){
this.fun = fun;
}
@Override
public void run() {
fun.run2();
}
}
修改的关键一点:Fun中的run2()方法不是静态的
结果:
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 0____Thread-2
11-13 16:22:10.410 31450-31466/? I/System.out: Hello: 0____Thread-3
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 1____Thread-2
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 2____Thread-2
11-13 16:22:10.410 31450-31466/? I/System.out: Hello: 1____Thread-3
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 3____Thread-2
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 4____Thread-2
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 5____Thread-2
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 6____Thread-2
11-13 16:22:10.410 31450-31465/? I/System.out: Hello: 7____Thread-2
11-13 16:22:10.410 31450-31466/? I/System.out: Hello: 2____Thread-3
11-13 16:22:10.410 31450-31466/? I/System.out: Hello: 3____Thread-3
11-13 16:22:10.410 31450-31466/? I/System.out: Hello: 4____Thread-3
11-13 16:22:10.411 31450-31466/? I/System.out: Hello: 5____Thread-3
11-13 16:22:10.411 31450-31466/? I/System.out: Hello: 6____Thread-3
11-13 16:22:10.411 31450-31466/? I/System.out: Hello: 7____Thread-3
11-13 16:22:10.411 31450-31465/? I/System.out: Hello: 8____Thread-2
11-13 16:22:10.411 31450-31466/? I/System.out: Hello: 8____Thread-3
11-13 16:22:10.411 31450-31465/? I/System.out: Hello: 9____Thread-2
11-13 16:22:10.411 31450-31466/? I/System.out: Hello: 9____Thread-3
可以看出又不同步了
为什么?
因为run()是静态的,run2()方法不是,这两个方法虽然都是用synchronized修饰,但是锁不是一把,run()的锁是class,而run2()的锁是class对象,不同的锁当然不能同步.
4.synchronized修饰代码块
这个和修饰普通方法差不多,只不过同步的地方是被synchronized包起来的的地方,它的锁取决于这个方法是否是静态的,我们常用的地方就是单例,举个例子:
public static PushProcess getInstance() {
if (instance == null) {
synchronized (PushProcess.class) {
if (instance == null) {
instance = new PushProcess();
}
}
}
return instance;
}
可能一个方法中只有几行代码会涉及到线程同步问题,所以synchronized块比synchronized方法更加细粒度地控制了多个线程的访问,只有synchronized块中的内容不能同时被多个线程所访问,方法中的其他语句仍然可以同时被多个线程所访问(包括synchronized块之前的和之后的)。