这是我参与8月更文挑战的第20天,活动详情查看:8月更文挑战
多线程一直Java开发中的难点,也是面试中的常客,趁着还有时间,打算巩固一下JUC方面知识,我想机会随处可见,但始终都是留给有准备的人的,希望我们都能加油!!!
沉下去,再浮上来,我想我们会变的不一样的。
synchronized 实现同步的基础:Java 中的每一个对象都可以作为锁。
一次偶然在家阳台上拍下来的,喜欢这样的天,让人舒缓
JUC系列
一、对于普通同步方法:
对于普通同步方法,锁的是当前实例对象。
一个对象里面如果有多个 synchronized非静态方法,某一个时刻内,只要一个线程去调用了其中的 一个用synchronized修饰的方法, 其它的线程都只能等待。换句话说,某一个时刻内,只能有唯一一个线程去访问这些 synchronized 方法。
(注:当只有一个对象实例时)。
public class Synchronized8Demo {
public static void main(String[] args) throws InterruptedException {
Demo demo = new Demo();
new Thread(()->{
demo.test1();
},"AA").start();
new Thread(()->{
demo.test2();
},"BB").start();
}
}
class Demo{
public synchronized void test1(){
try {
Thread.sleep(1000);
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+":: 循环第"+i+"次");
}
}catch (Exception e){
e.printStackTrace();
}
}
public synchronized void test2(){
System.out.println(Thread.currentThread().getName()+":: 只循环一次的方法");
}
}
运行结果:
通过运行结果我们看到,当A线程进入的由synchronized修饰的test1()的方法后,B线程只有等待A线程释放锁,才能进入由synchronized修饰的test2(),以此可以说明当只有一个实例对象时,一个对象里面如果有多个 synchronized非静态方法,某一个时刻内,只要一个线程去调用了其中的 一个用synchronized修饰的方法, 其它的线程都只能等待。
原因:因为锁的是当前对象 this,被锁定后,其它的线程都不能进入到当前对象的其它的 synchronized 方法
上面test1()的方法代码也可以换成这样,结果也是一样的。
public void test1() {
synchronized (this) {
try {
Thread.sleep(1000);
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":: 循环第" + i + "次");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
补充:
1、但是加个普通方法,是和同步锁无关的。
public class Synchronized8Demo {
public static void main(String[] args) throws InterruptedException {
Demo demo = new Demo();
new Thread(() -> {
demo.test1();
}, "AA").start();
new Thread(() -> {
demo.test2();
}, "BB").start();
new Thread(() -> {
demo.test3();
}, "CC").start();
}
}
class Demo {
public void test1() {
synchronized (this) {
try {
Thread.sleep(1000);
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":: 循环第" + i + "次");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public synchronized void test2() {
System.out.println(Thread.currentThread().getName() + ":: 只循环一次的方法");
}
public void test3(){
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"::没有synchronized关键字的普通方法");
}
}
}
运行结果:
结论:另外加一个普通方法,执行起来是完全和同步锁无关的,也无需等待。
2、另外如果换成两个对象,情况也会不一样
public class Synchronized8Demo {
public static void main(String[] args) throws InterruptedException {
Demo demo = new Demo();
new Thread(() -> {
demo.test1();
}, "AA").start();
Demo demo1 = new Demo();
new Thread(() -> {
demo1.test2();
}, "BB").start();
}
}
class Demo {
public void test1() {
synchronized (this) {
try {
Thread.sleep(1000);
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":: 循环第" + i + "次");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public synchronized void test2() {
System.out.println(Thread.currentThread().getName() + ":: 只循环一次的方法");
}
}
运行结果:
无需等待A线程释放锁,即可执行。
小结:
即如果一个实例对象的非静态同步方法得到锁之后,此实例对象的其他非静态同步方法必须一直等到获取到锁的方法释放锁之后,才能重新获取到锁。但是另外的实例对象的非静态同步方法和此实例对象用的并非一把锁,所以无需等待此实例对象已经获取到的锁的非静态同步方法释放锁就可以去获取属于他们的锁。
下面看看静态同步方法是什么样的吧👇
二、对于静态同步方法:
对于静态同步方法,锁是当前类的 Class 对象。
例如:我们修改一下上面的那个例子。把test2()方法改成静态方法,看看结果是什么样的吧。
public static synchronized void test2() {
System.out.println(Thread.currentThread().getName() + ":: 只循环一次的方法");
}
运行结果:
所有的静态同步方法用的也是同一把锁——类对象本身,而非静态同步方法锁的当前实例对象,所以这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会产生竞态条件的。
和之前一样,如果一个静态同步方法获取到锁之后,其他的静态同步方法都必须等待此方法释放锁之后,才能继续获取到锁。并且不管是一个实例对象的静态同步方法之间,还是不同的实例对象静态同步放之间,他们都必须遵守,因为他们是同一个类的实例对象。
此处代码书写方式勿怪,为测试刻意而为。
public class Synchronized8Demo {
public static void main(String[] args) throws InterruptedException {
Demo demo = new Demo();
new Thread(() -> {
demo.test1();
}, "AA").start();
Demo demo1 = new Demo();
new Thread(() -> {
demo1.test2();
}, "BB").start();
}
}
class Demo {
public static synchronized void test1() {
try {
Thread.sleep(1000);
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + ":: 循环第" + i + "次");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static synchronized void test2() {
System.out.println(Thread.currentThread().getName() + ":: 只循环一次的方法");
}
}
三、对于同步方法块:
对于同步方法块,锁是 Synchonized 括号里配置的对象
当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
测试一: 当锁的对象相同时,产生竞争条件,形成竞争状态。
public class Synchronized8Demo {
public static void main(String[] args) throws InterruptedException {
Demo demo = new Demo();
new Thread(()->{
demo.test4();
},"CC").start();
new Thread(()->{
demo.test5();
},"DD").start();
}
}
class Demo {
public void test4() {
synchronized (Demo.class){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"::测试同步代码块");
}
}
}
public void test5(){
synchronized (Demo.class){
System.out.println(Thread.currentThread().getName()+"::测试同步代码块");
}
}
}
测试二:当锁的对象不同时,无须等待test4()方法释放锁,即可执行test5()方法,
原因:因为不是同一把锁,不产生竞争条件。
public class Synchronized8Demo {
public static void main(String[] args) throws InterruptedException {
Demo demo = new Demo();
new Thread(()->{
demo.test4();
},"CC").start();
new Thread(()->{
demo.test5();
},"DD").start();
}
}
class Demo {
public void test4() {
synchronized (this){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"::测试同步代码块");
}
}
}
public void test5(){
synchronized (Demo.class){
System.out.println(Thread.currentThread().getName()+"::测试同步代码块");
}
}
}
小结:
对于同步方法块,关键就是 synchronized () 括号中锁的对象是谁。
四、自言自语
最近又开始了JUC的学习,感觉Java内容真的很多,但是为了能够走的更远,还是觉得应该需要打牢一下基础。
最近在持续更新中,如果你觉得对你有所帮助,也感兴趣的话,关注我吧,让我们一起学习,一起讨论吧。
你好,我是博主宁在春,Java学习路上的一颗小小的种子,也希望有一天能扎根长成苍天大树。
希望与君共勉😁
我们:待别时相见时,都已有所成。