java高并发之魂:synchronized

2019/01/03

场景 两个线程 i++ 十万次 结果是二十万?

public class DisappearRequest1 implements Runnable{
	static DisappearRequest1 instance = new DisappearRequest1();
	static int i=0;
	public static void main(String[] args) throws InterruptedException{
		Thread t1 = new Thread(instance);
		Thread t2 = new Thread(instance);
		t1.start();
		t2.start();
		//让父线程等待子线程结束之后才能继续运行。
		t1.join();
		t2.join();
		System.out.println(i);
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int j = 0; j < 100000; j++) {
			i++;
		}
		
	}

}

结果会比预计的少 原因

count++ 包含三个动作:

  1. 读取count
  2. 将count加一
  3. 将count值写入内存中 两个线程读到同一个count值 比如10,然后加1,都存的是11.这就是线程不安全

Synchronized的两种用法

  1. 对象锁
    • 方法锁
       @Override
       public void run() {
         method();
       }
       public synchronized void method(){
        System.out.println("我是对象锁的方法修饰符形式。我叫"+
            Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+" 运行结束");
       }
      
    • 同步代码块锁 使用synchronized(lock/this){代码} 同一把锁 线程串行。不同锁 线程并行
       Object lock1= new Object();
       Object lock2= new Object();
       @Override
       public void run() {
        synchronized (lock1) {
            System.out.println("我是lock1。 我叫"+
            Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }		
        }
        synchronized (lock2) {
            System.out.println("我是lock2。 我叫"+
            Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(	Thread.currentThread().getName()+" lock2运行结束");
        }
       }
      
  2. 类锁
    • 修饰静态的方法 概念:Java类可能有多个对象,但只有一个class对象 类锁的第一种形式:static synchronized
       public class SynchronizedClassStatic4 implements Runnable{
       static SynchronizedClassStatic4 instance1 =new SynchronizedClassStatic4();
       static SynchronizedClassStatic4 instance2 =new SynchronizedClassStatic4();
       @Override
       public void run() {
        // TODO Auto-generated method stub
        method();
       }
       public static synchronized void method(){
        System.out.println("我是类锁的第一种形是:static形式 我叫"+
       Thread.currentThread().getName());
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"运行结束");
       }
       public static void main(String[] args) throws InterruptedException {
        Thread t1= new Thread(instance1);
        Thread t2= new Thread(instance2);
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("over");
       }
       }
      
    • 指定锁为Class对象 synchronized(.class)–类锁的第二种形式 ```java public void run() { method(); } public void method(){ synchronized(SynchronizedClassClass5.class){ System.out.println(“我是类锁的第二种形是:synchronized(.class)形式 我叫”+ Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+”运行结束”); }

    } ```

    多线程访问同步方法的七种情况(面试常考)

  3. 连个线程同时访问一个对象的同步方法 串行
  4. 两个线程访问的是两个对象的同步方法 并行
  5. 两个线程访问的是synchronized的静态方法 串行
  6. 同时访问同步方法与非同步方法 非同步方法不受影响
  7. 访问同一个类的的不同的普通同步方法 串行
  8. 同时访问静态synchronized和非静态synchronized的方法 非静态synchronized不受影响
  9. 方法抛异常后,释放锁
  10. synchronized的方法 调用普通方法,那普通方法不是线程安全的