一、为什么需要synchronized
看下面这段代码
/**
* Created by yan on 2019/2/3.
*/
public class ThreadDemo implements Runnable {
static ThreadDemo threadDemo = new ThreadDemo();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(threadDemo);
Thread t2 = new Thread(threadDemo);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
static int i = 0;
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
i++;
}
}
}
正常情况下应该输出10000*2 = 20000
我运行三次看下结果
很明显它是不等20000的
原因:当t1线程读取到count++后在写入内存之前t2线程读取count++,因此两个线程读取的是同一个数字(这种概率是相当大的),因此最终获得的数字远小于预期的值
二、synchronized的两种用法
对象锁
包括方法锁(默认对象为this当前实例对象)和同步代码块锁(自己指定锁对象)
类锁
synchronized修饰的静态方法,或指定锁的class对象
2.1、对象锁
代码块形式:手动指定锁对象
方法锁形式:synchronized修饰普通方法,锁对象默认为this
2.1.1、代码块形式锁
/**
* Created by yan on 2019/2/3.
*/
public class SynchronizedObjectCodeBlok implements Runnable {
static SynchronizedObjectCodeBlok instance = new SynchronizedObjectCodeBlok();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()){ //当t1和t2两个线程都是存活状态时,做空循环
}
System.out.println("finished");
}
@Override
public void run() {
synchronized (this) {
System.out.println("对象锁代码块形式" + Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "运行完毕");
}
}
}
有synchronized代码块:对象锁代码块形式Thread-0-----5s-----Thread-0运行完毕,对象锁代码块形式Thread-1-----5s-----Thread-1运行完毕,finished
无synchronized代码块:两个线程“同时"运行,“同时”结束
如果指定锁对象?
创建锁对象
Object lock = new Object();
用lock取代synchronized中的this
运行效果:和this相同
那什么时候使用指定锁对象呢?
当有不止一个synchronized代码块时,可以指定不同的锁对象
/**
* Created by yan on 2019/2/3.
*/
public class SynchronizedObjectCodeBlok implements Runnable {
static SynchronizedObjectCodeBlok instance = new SynchronizedObjectCodeBlok();
//创建锁对象
Object lock1 = new Object();
Object lock2 = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()){ //当t1和t2两个线程都是存活状态时,做空循环
}
System.out.println("finished");
}
@Override
public void run() {
synchronized (lock1) {
System.out.println("对象锁代码块形式lock1--" + Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--lock1运行完毕");
}
synchronized (lock2) {
System.out.println("对象锁代码块形式lock2--" + Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--lock2运行完毕");
}
}
}
效果:
第一个线程拿到锁lock1—>第一个线程lock1执行完毕(“同时”第一个线程拿到lock2,第二个线程拿到lock1)—> 第一个线程的lock2和第二个线程的lock1“同时”执行完毕(“同时第二个线程拿到lock2”)—>第二个线程lock2执行完毕---->finished
如果是3个线程,更或者是10个线程呢?(那还不如去算彩票的中奖规律呢,之后再说!!!!)
2.1.2、方法锁
/**
* Created by yan on 2019/2/3.
*/
public class SynchronizedObjectMethod implements Runnable {
static SynchronizedObjectMethod instance = new SynchronizedObjectMethod();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()){ //当t1和t2两个线程都是存活状态时,做空循环
}
System.out.println("finished");
}
@Override
public void run() {
method();
}
//使用synchronized修饰方法,并在run方法中调用此方法
public synchronized void method(){
System.out.println("对象锁方法形式--"+Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--线程结束");
}
}
结果:第一个线程结束后第二个线程才开始运行
2.2、类锁
概念:java类中可能有很多个对象,但只有1个Class对象
类锁的使用方法:
1.synchronized加在static方法上
2.synchronized(*.class)代码块
2.2.1、synchronized加在static方法上
这里我们将类实例化了两次
/**
* Created by yan on 2019/2/4.
* 类锁---static形式
*/
public class SynchronizedClassStatic implements Runnable {
static SynchronizedClassStatic instance1 = new SynchronizedClassStatic();
static SynchronizedClassStatic instance2 = new SynchronizedClassStatic();
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while(t1.isAlive() || t2.isAlive()){
}
System.out.println("finished");
}
@Override
public void run() {
methed();
}
//synchronized加在static方法上,在run方法中调用
public static synchronized void methed(){
System.out.println("static形式--"+Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--运行结束");
}
}
有static的效果:第一个线程执行完后,第二个线程开始执行
五static的效果:两个线程“同时”开始,“同时”结束
2.2.2、synchronized(*.class)代码块
/**
* Created by yan on 2019/2/4.
* 类锁--- *.class
*/
public class SynchronizedClassClass implements Runnable {
static SynchronizedClassClass instance1 = new SynchronizedClassClass();
static SynchronizedClassClass instance2 = new SynchronizedClassClass();
public static void main(String[] args) {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while(t1.isAlive() || t2.isAlive()){
}
System.out.println("finished");
}
@Override
public void run() {
method();
}
public void method(){
synchronized (SynchronizedClassClass.class){
System.out.println("类锁*.class--"+ Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--线程结束");
}
}
}
有*.class效果:第一个线程结束后,第二个线程开始运行
换成 this :两个线程“同时”开始,“同时”结束
文章开篇的代码输出小于20000,我们学了sychronized的两大类–四种方法,感兴趣的可以试一下能不能得出20000
三、多线程访问同步方法的7种情况
1.两个线程同时访问一个对象的同步方法
2.两个线程同时访问两个对象的同步方法
3.两个线程同时访问synchronized的静态方法
4.两个线程同时访问同步方法和非同步方法
5.两个线程访问同一对象的不同的普通同步方法(非static)
6.两个线程同时访问静态synchronized和非静态synchronized方法
7.方法抛出异常后,会释放锁(延伸扩展内容)
3.1、两个线程同时访问一个对象的同步方法
3.2、两个线程同时访问两个对象的同步方法
两个对象指两个实例 instance1,instance2创建两个线程
/**
* Created by yan on 2019/2/3.
*/
public class SynchronizedObjectCodeBlok implements Runnable {
static SynchronizedObjectCodeBlok instance1 = new SynchronizedObjectCodeBlok();
static SynchronizedObjectCodeBlok instance2 = new SynchronizedObjectCodeBlok();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance1);
Thread t2 = new Thread(instance2);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()){ //当t1和t2两个线程都是存活状态时,做空循环
}
System.out.println("finished");
}
@Override
public void run() {
synchronized (this) {
System.out.println("对象锁代码块形式--" + Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--运行完毕");
}
synchronized (this) {
System.out.println("对象锁代码块形式--" + Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--运行完毕");
}
}
}
3.3、两个线程同时访问synchronized的静态方法
2.2.2.1、synchronized加在static方法上
3.4、两个线程同时访问同步方法和非同步方法
我们设定一个线程访问synchronized的同步方法,另一个线程访问非同步(普通)方法,看代码:
/**
* Created by yan on 2019/2/4.
*/
public class SynchronizedYesAndNoMethod implements Runnable {
static SynchronizedYesAndNoMethod instance = new SynchronizedYesAndNoMethod();
public static void main(String[] args) {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while(t1.isAlive() || t2.isAlive()){
}
System.out.println("finished");
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("Thread-0")){
method1();
}else{
method2();
}
}
//同步方法
public synchronized void method1(){
System.out.println("synchronized同步方法--"+Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--线程结束");
}
//非同步方法
public void method2(){
System.out.println("非同步(普通)方法--"+Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--线程结束");
}
}
两个线程“同时”开始,“同时”结束,意思就是*非同步方法不受影响
3.5、两个线程访问同一对象的不同的普通同步方法(非static)
/**
* Created by yan on 2019/2/4.
*/
public class SynchronizedDifferenceMethod implements Runnable {
static SynchronizedDifferenceMethod instance = new SynchronizedDifferenceMethod();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()){ //当t1和t2两个线程都是存活状态时,做空循环
}
System.out.println("finished");
}
@Override
public void run() {
method1();
method2();
}
public synchronized void method1(){
synchronized (SynchronizedClassClass.class){
System.out.println("方法1--"+ Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--线程结束");
}
}
public synchronized void method2(){
synchronized (SynchronizedClassClass.class){
System.out.println("方法2--"+ Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--线程结束");
}
}
}
方法1执行完两个线程,才执行方法2
3.6、两个线程同时访问静态synchronized和非静态synchronized方法(重要)
/**
* Created by yan on 2019/2/4.
*/
public class SynchronizedStaticAndNormal implements Runnable {
static SynchronizedStaticAndNormal instance = new SynchronizedStaticAndNormal();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while (t1.isAlive() || t2.isAlive()){ //当t1和t2两个线程都是存活状态时,做空循环
}
System.out.println("finished");
}
@Override
public void run() {
if(Thread.currentThread().getName().equals("Thread-0")){
method1();
}else{
method2();
}
}
public static synchronized void method1(){
System.out.println("static方法1--"+ Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--线程结束");
}
public synchronized void method2(){
System.out.println("非static方法2--"+ Thread.currentThread().getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--线程结束");
}
}
“同时”开始,“同时”结束
3.7、方法抛出异常后,会释放锁
不太容易表达,哎,睡觉!!!!!!!