java 多线程锁机制1–Synchrnized

2018年10月12日

原子性(Atomicity)

原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度,既不被中断操作,要不执行完成,要不就不执行。如果一个操作时原子性的,那么多线程并发的情况下,就不会出现线程安全问题;

例如a++,对于共享变量a的操作,实际上会执行三个步骤,

1.读取变量a的值到寄存器

2.寄存器中的值+1

3.将值赋予变量a

这三个操作中任何一个操作过程中,a的值被人篡改,那么都会出现我们不希望出现的结果。所以必须保证这是原子性的。Java中的锁的机制解决了原子性的问题

 

Java 提供多种锁机制做线程同步, 保证只有一个线程能进入临界区:

  • Synchrnized 关键字:

使用方式:

  • 指定加锁对象: 对给定Object 加锁, 进入同步代码前要取得该对象的锁;
  • Object.wait():释放当前对象锁,当前线程 进入阻塞队列
  • Object.notify():唤醒当前对象阻塞队列里的任一线程(并不保证唤醒哪一个)
  • Object.notifyAll():唤醒当前对象阻塞队列里的所有线程
public class ThreadSyncObject{
    final static Object object=new Object();

    public static class T1 extends Thread{
        public void run(){
            synchronized (object){
                System.out.println("T1 start!");
                try{
                    System.out.println("T1 wait for object !");
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("T1 end !");
            }
        }
    }

    public static class T2 extends Thread{
        public void run(){
            synchronized (object){
                System.out.println("T2 start! notify one thread");
                object.notifyAll();// 如果是object.notify()则只会通知一个T1线程,另外一个T1线程仍然在等待队列中,无法结束
                System.out.println("T2 end !");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }



    public static void main(String args[]) throws InterruptedException {
        Thread t1 = new T1();
        Thread t1_1 = new T1();

        t1.start();
        t1_1.start();
        Thread.sleep(1000);

        Thread t2 = new T2();
        t2.start();
        t1.join();
        t2.join();
    }
}
  • 如果使用object.notifyAll(), 两个T1线程都能正常从阻塞队列中恢复并结束:
    T1 start!
    T1 wait for object !
    T1 start!
    T1 wait for object !
    T2 start! notify one thread
    T2 end !
    T1 end !
    T1 end !
    
    Process finished with exit code 0
    

    如果使用object.notify(), 始终会有一个T1线程一直在阻塞队列中,导致主进程无法结束:

    T1 start!
    T1 wait for object !
    T1 start!
    T1 wait for object !
    T2 start! notify one thread
    T2 end !
    T1 end !

 

  • 对实例方法加锁:  给当前实例的方法加锁, 进入同步代码前要取得该对象的锁;
public class ThreadSyncFunc implements Runnable {
    static  int i=0;

    public synchronized void add(){
        i++;
    }

    @Override
    public void run() {
        for (int j=0; j<1000000; j++){
            add();
        }
    }

    public static void main(String args[]) throws InterruptedException {
        ThreadSyncFunc mt = new ThreadSyncFunc();
        Thread t1 = new Thread(mt, "t1");
        Thread t2 = new Thread(mt, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

增加了同步操作的执行结果, 计算结果符合预期,等于2*1000000:

2000000

Process finished with exit code 0

如果add() 函数去掉”synchronized “,执行结果总是<2000000

1989320

Process finished with exit code 0

 

 

  • 对static 方法加锁: 给当前类方法加锁,进入同步代码前要取得该对象的锁;
public class ThreadSyncStaticFunc implements Runnable {
    static  int i=0;

    public static synchronized void add(){
        i++;
    }

    @Override
    public void run() {
        for (int j=0; j<1000000; j++){
            add();
        }
    }

    public static void main(String args[]) throws InterruptedException {
        ThreadSyncStaticFunc mt1 = new ThreadSyncStaticFunc();
        ThreadSyncStaticFunc mt2 = new ThreadSyncStaticFunc();//因为是2个实例, 需要共同操作一个i类变量, 也需要把add()设置成类函数
        Thread t1 = new Thread(mt1, "t1");
        Thread t2 = new Thread(mt2, "t2");

        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(i);
    }
}

这个样例执行结果, 计算结果符合预期,等于2*1000000:

2000000

Process finished with exit code 0

如果去掉synchronized 结果也总是<2000000

1501370

Process finished with exit code 0

 


 

 

没有评论

发表评论

邮箱地址不会被公开。 必填项已用*标注