Synchronized
线程安全的主要诱因:
-
存在共享数据(临界资源)
-
存在多条线程共同操作这些共享数据,读写操作时发生指令交错
-
例如:两个线程对同一个变量进行i++和i–操作,因为每一个操作都会被编译为多条字节码,字节码运行完之后,再回写到主内存的变量中,而多线程的指令会被CPU交错执行导致线程不安全。
线程安全分析
成员变量和静态变量线性安全?
- 非共享,则线程安全
- 共享,读操作线程安全、读写操作则需考虑线程安全
局部变量线性安全?
- 局部变量线程安全,局部变量在每个线程的栈帧中,不存在共享
- 局部变量引用对象(对象在堆中可能会被共享),对象未在方法体作用域外(即每个线程都会new一个对象,则都是操作的各自堆中的对象)线程安全、否则(多个线程操作同一个对象,有临界区域)需考虑线程安全
常见线程安全类
一下方法都加了Synchronized因此单个操作都是线程安全,但是组合起来不是原子的不能保证线程安全。
- String
- Integer
- StringBuffer
- Random
- Vector
- Hashtable
- JUC并发包下的类
- 不可变类
解决线程安全问题的根本方法:
- 同一时刻有且只有一个线程操作共享数据,其他线程必须等到该线程处理完数据后再对堆共享数据进行操作。
解决线程安全问题的具体方法:
- 阻塞式的解决方案:synchronized(俗称对象锁,对象锁保证了临界区代码的原子性,临界区内的代码是不可分割的、不会被线程切换所打断)、Lock
- 非阻塞式的解决方案:原子变量
synchronized实现的互斥锁的特性:
- 互斥性,即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程的协调机制,这样同一时间只有一个线程对需要同步的代码块(复合操作)进行访问,互斥性也称为操作的原子性。
- 可见性,必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作,从而引起不一致。同volatile的特性一致。
- Synchronized锁的不是代码,锁的是对象。
同步与互斥
- java中同步和互斥都采用**synchronized关键字来完成,但是还是有区别
- 互斥是保证临界区竞态条件发生,同一时刻只能有一个线程执行临界区代码
- 同步是由于线程执行的先后、顺序不同,需要一个线程等待其他线程运行到某个点
获取的锁的分类:获取对象锁和获取类锁
-
获取对象锁
- 1.同步代码块(synchronized(this),synchronized(实例对象),锁是小括号中的实例对象)。
- 2.同步非静态方法(synchronized method),锁是当前对象的实例对象。
-
获取类锁,每个类只有一个类锁
- 1.同步代码块(synchronized(类.class)),锁是小括号中的类的对象(Class对象)。
- 2.同步静态方法(synchronized static method),锁是当前对象的类对象(Class对象)。
类锁和对象锁的总结
-
有线程访问对象的同步代码块时,另外的线程可以访问该对象的非同步代码块。
-
若锁住的是一个对象,一个线程在访问对象的同步代码块或同步方法时,另一个访问对象的同步代码块或同步方法的线程会被阻塞
-
同一个类的不同对象锁互不干扰
-
类锁由于是一种特殊的对象锁,因此特性和对象锁类似,而由于一个类只有一个对象锁,所以同一个类的不同对象使用类锁将会是同步的
-
类锁和对象锁互不干扰
package com.Interview.javabasic.thread; import java.text.SimpleDateFormat; import java.util.Date; public class SynThread implements Runnable { @Override public void run() { String threadName = Thread.currentThread().getName(); if (threadName.startsWith("A")){ async(); }else if(threadName.startsWith("B")){ synObjectBlock(); }else if(threadName.startsWith("C")){ synObjectMethod(); }else{ synClassBlock(); } } /** * 异步方法 */ private void async(){ try { System.out.println(Thread.currentThread().getName()+"_Async_Start_"+new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"_Async_End_"+new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 同步代码块,synchronized(this) */ private void synObjectBlock() { System.out.println(Thread.currentThread().getName() + "synObjectBlock" + new SimpleDateFormat("HH:mm:ss").format(new Date())); synchronized (this) { try { System.out.println(Thread.currentThread().getName() + "_synObjectBlock_Start_" + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_synObjectBlock_End_" + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 同步非静态方法(synchronized method) */ private synchronized void synObjectMethod(){ System.out.println(Thread.currentThread().getName() + "synObjectMethod" + new SimpleDateFormat("HH:mm:ss").format(new Date())); try { System.out.println(Thread.currentThread().getName() + "_synObjectMethod_Start_" + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_synObjectMethod_End_" + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 同步代码块(synchronized(类.class)) */ private void synClassBlock(){ System.out.println(Thread.currentThread().getName() + "synClassBlock" + new SimpleDateFormat("HH:mm:ss").format(new Date())); synchronized (SynThread.class) { try { System.out.println(Thread.currentThread().getName() + "_synClassBlock_Start_" + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_synClassBlock_End_" + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 同步静态方法(synchronized static method) */ private synchronized static void syncClassMethod(){ System.out.println(Thread.currentThread().getName() + "syncClassMethod" + new SimpleDateFormat("HH:mm:ss").format(new Date())); try { System.out.println(Thread.currentThread().getName() + "_syncClassMethod_Start_" + new SimpleDateFormat("HH:mm:ss").format(new Date())); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "_syncClassMethod_End_" + new SimpleDateFormat("HH:mm:ss").format(new Date())); } catch (InterruptedException e) { e.printStackTrace(); } } }
线程八锁-实例
synchronized的基础
- Java对象头
- Monitor