java多线程(四)数据同步与synchronized

一.问题引出

TicketWindowRunnable.java

package com.miracle.concurrency.chapter7;


public class TicketWindowRunnable implements Runnable{

    private int index = 1;

    private final static int MAX = 50;

    @Override
    public void run() {

        while (true) {
            if (index > MAX){
                break;
            }

            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread() + " 的号码是:" + (index++));
        }
    }
}

BankVersion.java

package com.miracle.concurrency.chapter7;


public class BankVersion {

    public static void main(String[] args) {
        final TicketWindowRunnable ticketWindow = new TicketWindowRunnable();

        Thread windowThread1 = new Thread(ticketWindow, "一号窗口");
        Thread windowThread2 = new Thread(ticketWindow, "二号窗口");
        Thread windowThread3 = new Thread(ticketWindow, "三号窗口");

        windowThread1.start();
        windowThread2.start();
        windowThread3.start();
    }
}

执行BankVersion
在这里插入图片描述
发现超过50了,由此引出多线程安全问题。此问题一般发生于多个线程操作同一数据

二.synchronized关键字

1.理解

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性),即可以代替volatile。

2.synchronized的三种应用方式

Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:

  • 1)普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
  • 2)静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
  • 3)同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

3.synchronized代码示例

  • 上面线程安全问题可以通过下面代码解决
package com.miracle.concurrency.chapter7;


public class TicketWindowRunnable implements Runnable{

    private int index = 1;

    private final static int MAX = 50;

    private final Object MONITOB = new Object();

    @Override
    public void run() {

        while (true) {

            synchronized (MONITOB){
                if (index > MAX){
                    break;
                }

                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread() + " 的号码是:" + (index++));
            }
        }
    }
}

4.synchronized this锁

synchronized 加在代码块上时,需要指定锁对象,当synchronized加在方法上时,不需要(这是使用的是this锁)

SynchronizedThis.java

package com.miracle.concurrency.chapter7;


public class SynchronizedThis {

    public static void main(String[] args) {
        ThisLock thisLock = new ThisLock();
        
        /*
            这里开启两个线程,分别执行ThisLock.m1,   
            ThisLock.m2 方法
            由于 synchronized 加在方法上时,默认使用this对象当锁
            导致两个线程用一个锁(虽然执行的不是同一个方法)
            执行结果为两个线程顺序执行
         */
        new Thread("T1"){
            @Override
            public void run() {
                thisLock.m1();
            }
        }.start();

        new Thread("T2"){
            @Override
            public void run() {
                thisLock.m2();
            }
        }.start();
    }

}

class ThisLock{

    public synchronized void m1(){
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(10_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void m2(){
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(10_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

接下来改写成代码块锁的形式
SynchronizedThis.java

package com.miracle.concurrency.chapter7;


public class SynchronizedThis {

    public static void main(String[] args) {
        ThisLock thisLock = new ThisLock();
        
        new Thread("T1"){
            @Override
            public void run() {
                thisLock.m1();
            }
        }.start();

        new Thread("T2"){
            @Override
            public void run() {
                thisLock.m2();
            }
        }.start();
    }

}

class ThisLock{

    private final Object LOCK = new Object();

    public synchronized void m1(){
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(10_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void m2(){
        synchronized(LOCK){
            try {
                System.out.println(Thread.currentThread().getName());
                Thread.sleep(10_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

5.synchronized class锁

当synchronized加到静态方法上时,用的是class锁

SynchronizedStatic.java

package com.miracle.concurrency.chapter7;


public class SynchronizedStatic {

    static {
        synchronized (SynchronizedStatic.class) {
            System.out.println("static " + Thread.currentThread().getName());
            try {
                Thread.sleep(10_000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized static void m1(){
        System.out.println("m1 " + Thread.currentThread().getName());
        try {
            Thread.sleep(10_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized static void m2(){
        System.out.println("m2 " + Thread.currentThread().getName());
        try {
            Thread.sleep(10_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void m3(){
        System.out.println("m3 " + Thread.currentThread().getName());
        try {
            Thread.sleep(10_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试类
SynchronizedStaticTest.java

package com.miracle.concurrency.chapter7;


public class SynchronizedStaticTest {

    public static void main(String[] args) {
        new Thread("T1"){
            @Override
            public void run() {
                SynchronizedStatic.m1();
            }
        }.start();
        new Thread("T2"){
            @Override
            public void run() {
                SynchronizedStatic.m2();
            }
        }.start();
        new Thread("T3"){
            @Override
            public void run() {
                SynchronizedStatic.m3();
            }
        }.start();
    }
}

三.死锁问题

1.死锁示例

DeadLock.java

package com.miracle.concurrency.chapter8;


public class DeadLock {

    private OtherService otherService;

    public DeadLock(OtherService otherService) {
        this.otherService = otherService;
    }

    private final Object LOCK = new Object();

    public void m1(){
        synchronized (LOCK){
            System.out.println("m1");
            otherService.s1();
        }
    }

    public void m2(){
        synchronized (LOCK){
            System.out.println("m2");
        }
    }
}

OtherService.java

package com.miracle.concurrency.chapter8;


public class OtherService {

    private final Object LOCK = new Object();

    private DeadLock deadLock;

    public void s1() {
        synchronized (LOCK) {
            System.out.println("s1=============");
        }
    }

    public void s2(){
        synchronized (LOCK) {
            System.out.println("s2=============");
            deadLock.m2();
        }
    }

    public void setDeadLock(DeadLock deadLock) {
        this.deadLock = deadLock;
    }
}

测试程序
DeadLockTest.java

package com.miracle.concurrency.chapter8;


public class DeadLockTest {
    public static void main(String[] args) {
        OtherService otherService = new OtherService();
        DeadLock deadLock = new DeadLock(otherService);
        otherService.setDeadLock(deadLock);

        new Thread(){
            @Override
            public void run() {
                while (true){
                    deadLock.m1();
                }
            }
        }.start();

        new Thread(){
            @Override
            public void run() {
                while (true) {
                    otherService.s2();
                }
            }
        }.start();
    }
}

2.查看程序中的死锁

在终端命令行上执行:jps找到程序的java进程号
在这里插入图片描述
执行:jstack + 刚才的进程号
在这里插入图片描述
在这里插入图片描述
根据这里发现两个线程产生死锁

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值