参考:
文章出处于:【Java并发编程之深入理解】Synchronized的使用
以下内容都经过实验。
在并发编程中存在线程安全问题,主要原因有:1.存在共享数据 2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。
可以用于解决Java并发问题,可以确保线程互斥的访问同步代码。
Java中每个对象都可以作为锁,这是synchronized实现同步的基础。
使用方法:
- 普通同步方法:锁是当前实例对象。当进入该对象的一个同步方法,则不能同时再进入该对象的另一个同步方法,可以进入非同步方法。
- 静态同步方法:锁是当前类,即class对象,进入同步代码前需要获得当前类锁。
- 同步方法块:所示括号里的对象,对给定对象加锁,进入同步代码块之前要获得该对象的对象锁。
https://www.jianshu.com/p/34fbf751133c
https://blog.csdn.net/sailor125/article/details/100140497
/**
* @description: 该类用于synchronized关键词测试
* @modifyContent:
* @author: Maple Chan
* @date: 2020-07-26 21:42:24
* @version: 0.0.1
*/
public class JavaSynchronizedTest implements Runnable {
/**
* 共享资源
*/
static int i = 0;
/**
* synchronized 修饰实例方法
*/
public synchronized void increaseSync() {
i++;
}
public void increase() {
i++;
}
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
// increase();
increaseSync();
}
}
public static void main(String[] args) throws InterruptedException {
JavaSynchronizedTest test = new JavaSynchronizedTest();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();
t2.start();
t1.join();
t2.join();
// 等待两个线程执行完,在执行下面输出
System.out.println(i);
}
}
/*
如果调用的是increaseSync(); 输出20000
如果调用的是increase(); 输出不确定,12317(12927),比20000小
*/
一个获取了对象锁后,其他线程就需要等该线程释放该对象锁之后,再由其他线程调用同步方法。
package javatest;
/**
* @description: 该类用于synchronized关键词测试
* @modifyContent:
* @author: Maple Chan
* @date: 2020-07-26 21:42:24
* @version: 0.0.1
*/
public class JavaSynchronizedTest implements Runnable {
public synchronized void methodSync1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void methodSync2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) throws InterruptedException {
new Thread(test::methodSync1).start();
new Thread(test::methodSync2).start();
}
}
/*
输出:
Method 1 start
Method 1 execute
Method 1 end
Method 2 start
Method 2 execute
Method 2 end
*/
一个线程获取对象所进入同步方法后,依旧可以直接调用非同步方法,不需要锁。
/**
* @description: 该类用于synchronized关键词测试
* @modifyContent:
* @author: Maple Chan
* @date: 2020-07-26 21:42:24
* @version: 0.0.1
*/
public class JavaSynchronizedTest implements Runnable {
public synchronized void methodSync1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void methodSync2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public synchronized void methodSync3() {
System.out.println("Method 3 start");
try {
System.out.println("Method 3 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 3 end");
}
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
increase();
// increaseSync();
}
}
public static void main(String[] args) throws InterruptedException {
JavaSynchronizedTest test = new JavaSynchronizedTest();
// ---------------------
new Thread(test::methodSync1).start();
new Thread(test::methodSync2).start();
// --------------------
new Thread(test::methodSync1).start();
new Thread(test::methodSync3).start();
}
}
/*
// 可以的出,2必须等1执行完毕才能执行,而3可以不用管1执行到哪儿直接进入执行非同步方法。
Method 1 start
Method 1 execute
Method 3 start
Method 3 execute
Method 3 end
Method 1 end
Method 1 start
Method 1 execute
Method 1 end
Method 2 start
Method 2 execute
Method 2 end
*/
不同对象的同步方法,互不影响,因为他们各自的对象锁不一样
package javatest;
/**
* @description: 该类用于synchronized关键词测试
* @modifyContent:
* @author: Maple Chan
* @date: 2020-07-26 21:42:24
* @version: 0.0.1
*/
public class JavaSynchronizedTest implements Runnable {
public synchronized void methodSync1() {
System.out.println("Method 1 start");
try {
System.out.println("Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 1 end");
}
public synchronized void methodSync2() {
System.out.println("Method 2 start");
try {
System.out.println("Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Method 2 end");
}
public static void main(String[] args) throws InterruptedException {
JavaSynchronizedTest test = new JavaSynchronizedTest()
JavaSynchronizedTest test2 = new JavaSynchronizedTest();
new Thread(test::methodSync1).start();
new Thread(test2::methodSync2).start();
}
}
/*
输出:
Method 1 start
Method 1 execute
Method 2 start
Method 2 execute
Method 2 end
Method 1 end
*/
静态方法的锁是类锁,一个线程进入静态方法之后,另一个线程需要等待该线程执行完毕才可以进入。
package javatest;
/**
* @description: 该类用于synchronized关键词测试
* @modifyContent:
* @author: Maple Chan
* @date: 2020-07-26 21:42:24
* @version: 0.0.1
*/
public class JavaSynchronizedTest implements Runnable {
/**
* 共享资源
*/
static int i = 0;
/**
* synchronized 修饰实例方法
*/
public synchronized void increaseSync() {
i++;
}
public void increase() {
i++;
}
public static synchronized void increaseStatic(){
i++;
}
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
// increase();
// increaseSync();
increaseStatic();
}
}
public static void main(String[] args) throws InterruptedException {
JavaSynchronizedTest test = new JavaSynchronizedTest();
JavaSynchronizedTest test2 = new JavaSynchronizedTest();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test2);
t1.start();
t2.start();
// t1.join();
// t2.join();
// System.out.println(i);
// 等待2s,等待两个线程执行完
Thread.sleep(2000);
System.out.println(i);
}
}
/*
两个线程实例化两个不同的对象,但是访问的方法是静态的,两个线程发生了互斥(即一个线程访问,另一个线程只能等着),因为静态方法是依附于类而不是对象的,当synchronized修饰静态方法时,锁是class对象。
输出:
20000
*/
在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失。此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了。
/**
* @description: 该类用于synchronized关键词测试
* @modifyContent:
* @author: Maple Chan
* @date: 2020-07-26 21:42:24
* @version: 0.0.1
*/
public class JavaSynchronizedTest implements Runnable {
/**
* 共享资源
*/
static int i = 0;
public void increase() {
i++;
}
@Override
public void run() {
synchronized (lock) {
for (int j = 0; j < 10000; j++) {
increase();
// increaseSync();
// increaseStatic();
}
}
}
public static void main(String[] args) throws InterruptedException {
JavaSynchronizedTest test2 = new JavaSynchronizedTest();
Thread tt1 = new Thread(test2);
Thread tt2 = new Thread(test2);
tt1.start();
tt2.start();
tt1.join();
tt2.join();
System.out.println(i);
}
}
/*
increase()是非同步方法,加了同步代码块之后,两个线程能同步执行
输出:
20000
*/