synchronized用法,简单明了

synchronized用法,简单明了(新手入)

synchronized是Java中的关键字,是一种同步锁。首先需要明白什么是同步?什么是异步?

1.线程同步和异步

首先说一下线程cpu之间的联系,线程是cpu调度和分派的基本单位。对于单核cpu来说,同一时间点或时间片段只能执行一个线程的工作,该时间片段是非常非常非常小的。又由于cpu执行速度特别的快,以至于多个线程抢得cpu执行权并执行功能的过程,像是在同时进行一样。
这时候同步和异步的概念就产生了。
线程同步(我有cpu执行权,你们不能抢劫我):多个线程抢夺cpu的执行权,当有一个线程A抢夺到了cpu执行权,其他线程就不能和线程A再进行抢夺,只能等待线程A**主动放弃**时,才可以重新抢夺,通常情况下A完成自己的使命会主动放弃cpu执行权。
线程异步(哪里有执行权,哪里就有抢夺):多个线程抢夺cpu的执行权,当有一个线程A抢夺到了cpu执行权,其他线程依然可以进行cpu的抢夺,即使A没有完成自己的使命。

2.synchronized是控制同步的关键所在,三要素:作用范围,线程对象,锁对象。线程对象拿到了锁在作用范围执行任务。

3.synchronized用法

  • 修饰代码块
  • 修饰实例方法
  • 修饰静态方法
  • 修饰类

4.synchronized修饰代码块

(1)格式:
synchronized(锁对象){ // 代码体现... }
(2)规则:只要锁对象是同一个对象,就会实现同步的效果。
(3)代码体现:

package cn.com.sync.block;
/**
 *  同步代码块,其作用的范围是大括号{}括起来的代码
 *  同步效果:线程t1或t2执行完循环后,另一个线程才进入执行
 */
public class SyncBlockThread implements Runnable{
    public static int count;
    public SyncBlockThread(){ count = 0; }
    @Override
    public void run() {
        synchronized(this){
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName()+":"+count++);
            }
        }
    }
    // 主函数
    public static void main(String[] args) {
        SyncBlockThread s = new SyncBlockThread();
        Thread t1= new Thread(s, "t1").start();
        Thread t2= new Thread(s, "t2").start();
    }
}

执行结果:只有这两种结果(同步效果)
t1:0————————-t2:0
t1:1————————-t2:1
t1:2————————-t2:2
t1:3————————-t2:3
t1:4————————-t2:4
t2:5———-或————t1:6
t2:6————————-t1:6
t2:7————————-t1:7
t2:8————————-t1:8
t2:9————————-t1:9

(4)结果分析:
锁对象是this,即SyncBlockThread实例对象s,作用范围是{}大括号中的循环,线程对象是t1和t2。如果t1先抢到锁对象,则先输出“t1:count”,直到循环结束放弃锁对象,t2才能拿到锁对象,执行自己的循环任务。
(5)锁对象不同时:

// 主函数
    public static void main(String[] args) {
        SyncBlockThread s1 = new SyncBlockThread();
        SyncBlockThread s2 = new SyncBlockThread();
        Thread t1 = new Thread(s1, "t1");
        Thread t2 = new Thread(s2, "t2");
        t1.start();
        t2.start();
    }

结果:
t1:0
t2:1
t1:2
t1:4
t2:3
t2:6
t1:5
t1:8
t2:7
t2:9
线程t1和t2的锁对象虽然都是SyncBlockThread类的实例对象(this),但是它们是两个不同的对象,t1拿到的锁是s1,t2拿到的锁是s2,锁对象不相同即不同步,所以方法没有实现同步的效果。思考:如果锁对象相同,但是方法不同呢?比如执行的是同一个类中两个不同的方法或不同类中的两个不同的方法?我们来实验一下不同类中的方法!

package cn.com.sync.block;
public class SyncBlockThread2 implements Runnable{
    @Override
    public void run() {
        synchronized("1"){
            for (int i = 0; i < 5; i++) {
                System.out.println("SyncBlockThread2类(" + 
                        Thread.currentThread().getName()+":"+SyncBlockThread.count++ + ")");
            }
        }
    }
}

创建第二个和SyncBlockThread类差不多的类SyncBlockThread2,同时将SyncBlockThread类中的输出语句修改为:System.out.println(“SyncBlockThread类(” + Thread.currentThread().getName()+”:”+count++ + “)”);并修改锁对象同为字符串“1”

public static void main(String[] args) {
        SyncBlockThread s1 = new SyncBlockThread();
        SyncBlockThread2 s2 = new SyncBlockThread2();
        Thread t1 = new Thread(s1, "t1");
        Thread t2 = new Thread(s2, "t2");
        t1.start();
        t2.start();
    }

结果:不同类的情况下,锁对象都是”1”,依然是实现了同步效果,说明即使是不同的类,锁对象相同的话也会有同步的效果。但这是同包中,如果是在不同的包中呢?小伙伴们可以测试一下
SyncBlockThread类(t1:0)
SyncBlockThread类(t1:1)
SyncBlockThread类(t1:2)
SyncBlockThread类(t1:3)
SyncBlockThread类(t1:4)
SyncBlockThread2类(t2:5)
SyncBlockThread2类(t2:6)
SyncBlockThread2类(t2:7)
SyncBlockThread2类(t2:8)
SyncBlockThread2类(t2:9)

5.synchronized修饰实例方法

(1)格式:
public synchronized void method(){}
(2)规则:只要是同一个实例对象,就会实现同步的效果。
(3)代码体现:

package cn.com.sync.method;
public class SyncMethodTrhead implements Runnable{
    public static int count = 0;
    @Override
    public void run() {
        method();
    }
    public synchronized void method(){
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+count++);
        }
    }
    public static void main(String[] args) {
        SyncMethodTrhead s1 = new SyncMethodTrhead();
        new Thread(s1,"t1").start();
        new Thread(s1,"t2").start();
    }
}

(4)结果:实现同步,其实这时候相当于锁对象就是实例对象本身,实例对象相同,即锁对象相同,实现同步

如果是两个不同的对象呢,

public static void main(String[] args) {
        SyncMethodTrhead s1 = new SyncMethodTrhead();
        SyncMethodTrhead s2 = new SyncMethodTrhead();
        new Thread(s1,"t1").start();
        new Thread(s1,"t2").start();
    }

结果是没有实现同步的效果。
t1:0
t2:0
t1:1
t2:2
t1:3
t2:4
t1:5
t2:6
t1:7
t2:8

6.synchronized修饰静态方法

(1)格式:
public synchronized void method(){}
public void synchronized method(){}
(2)规则:锁对象是针对这个类而言,这个类的所有对象都共用同一把锁。
(3)代码体现:

package cn.com.sync.static_method;
public class SyncStaticMethodThread implements Runnable{
    public static int count = 0;
    @Override
    public void run() {
        static_method();
    }
    public synchronized static  void  static_method(){
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+":"+count++);
        }
    }
    public static void main(String[] args) {
        SyncStaticMethodThread s1 = new SyncStaticMethodThread();
        SyncStaticMethodThread s2 = new SyncStaticMethodThread();
        new Thread(s1, "t1").start();
        new Thread(s2, "t2").start();
    }
}

(4)结果:该类的所有实例化对象都共用同一把锁,所以只要有线程执行该静态方法,都会同步阻塞进行
t1:0
t1:1
t1:2
t1:3
t1:4
t2:5
t2:6
t2:7
t2:8
t2:9

7.synchronized修饰类

(1)格式:
synchronized(SyncClazzThread.class){}
(2)规则:锁对象是针对这个类,这个类的所有对象都共用同一把锁(同修饰静态方法一样)。
(3)代码体现:

package cn.com.sync.clazz;
public class SyncClazzThread implements Runnable{
    public static int count = 0;
    public void method(){
        synchronized(SyncClazzThread.class){
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + count++);
            }
        }
    }
    @Override
    public void run() {
        method();
    }
    public static void main(String[] args) {
        SyncClazzThread s1 = new SyncClazzThread();
        SyncClazzThread s2 = new SyncClazzThread();
        new Thread(s1, "t1").start();
        new Thread(s2, "t2").start();
    }
}

(4)结果:同样的,该类的所有实例化对象都共用同一把锁,所以只要是执行该类实例对象的此方法,都会同步阻塞进行
t1:0
t1:1
t1:2
t1:3
t1:4
t2:5
t2:6
t2:7
t2:8
t2:9

8.总结

无论synchronized关键字加在方法上还是代码块上,只要锁对象相同就会同步阻塞执行。如果它作用的对象是非静态的,则它取得的锁就是针对对象而言,只要对象相等,锁就相同,锁相同,就同步;
如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是针对类而言,只要是该类的实例,它们就共用同一把锁。只要锁相同,就同步。
另外 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

你可能感兴趣的:(java,案例)