Java之线程的同步与锁

为什么要同步处理线程?

什么是同步?
同步指的是所有的线程不是一起进入到方法中执行,而是按照顺序一个一个进来。
范例:多个线程同步卖票

class MyThread implements Runnable {
    private int tickte = 10;

    @Override
    public void run() {
        while (tickte > 0) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "还剩" + this.tickte + "张票");
            this.tickte--;
        }
    }

}

class Test{
    public static void main(String[] args)throws InterruptedException {
       MyThread myThread=new MyThread();
       Thread thread1=new Thread(myThread,"线程A");
        Thread thread2=new Thread(myThread,"线程B");
        Thread thread3=new Thread(myThread,"线程C");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

运行结果:
在这里插入图片描述
观察上述代码的运行结果,我们发现票数居然出现了负数。为什么会这个样子呢?因为我们创建的三个线程同时卖票,他们会同时进入run()方法中,同时买一张票,假如说当现在还有2张票,但是由于三个线程同时卖一张票,线程A卖了一张票本来就剩一张票了,但是线程B和线程C都已经进入了while循环,还以为有两张票继续卖,这个操作叫做不同步操作。不同步的唯一好处是处理速度块(多线程并发执行)。

同步处理

synchronized处理同步问题

为了实现同步处理,我们可以进行加锁操作,采用关键字synchronized来处理。使用synchronized关键字处理有两种模式:同步代码块、同步方法。

使用同步代码块

如果要使用同步代码块必须设置一个要锁定的对象,所以一般可以锁定当前对象:this
范例:

class MyThread implements Runnable {
    private int tickte = 10;//一共10张票

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
        //在同一时刻,只允许一个线程进入代码块处理
            synchronized (this) {
                if (tickte > 0) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }//模拟网络延迟
                    System.out.println(Thread.currentThread().getName() + "还剩" + this.tickte + "张票");
                    this.tickte--;
                }
            }
        }
    }
}

class Test{
    public static void main(String[] args)throws InterruptedException {
       MyThread myThread=new MyThread();
       Thread thread1=new Thread(myThread,"线程A");
        Thread thread2=new Thread(myThread,"线程B");
        Thread thread3=new Thread(myThread,"线程C");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

运行结果:
在这里插入图片描述

使用同步方法

范例:

class MyThread implements Runnable {
    private int tickte = 10;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            sale();
        }
    }
    
    //同步方法
    public synchronized void sale(){
        if (tickte > 0) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "还剩" + this.tickte + "张票");
            this.tickte--;
        }
    }
}

class Test{
    public static void main(String[] args)throws InterruptedException {
       MyThread myThread=new MyThread();
       Thread thread1=new Thread(myThread,"线程A");
        Thread thread2=new Thread(myThread,"线程B");
        Thread thread3=new Thread(myThread,"线程C");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

同步虽然可以保证数据的完整性(线程安全操作),但是其执行的速度会很慢
注解:看完了上述两种锁操作,你一定会有一个疑问就是----为什么只有上锁操作没有解锁操作呢?其实它有一个隐式解锁操作,就是执行完锁里面的代码后,会自动解锁。

synchronized锁多对象

范例:synchronized锁多对象

class Sync{
    //锁的是当前对象this
    public synchronized void test(){
        System.out.println("test方法开始,当前线程为 "+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("test方法结束,当前线程为 "+Thread.currentThread().getName());
    }
}
class MyThread implements Runnable {

    @Override
    public void run() {
       Sync sync=new Sync();
       sync.test();
    }
}
class Test{
    public static void main(String[] args)throws InterruptedException {
      MyThread myThread=new MyThread();
       Thread thread1=new Thread(myThread);
        Thread thread2=new Thread(myThread);
        Thread thread3=new Thread(myThread);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

运行结果:
在这里插入图片描述
观察上述代码,我们发现没有锁住,三个线程同时运行test()方法。因为每一个进程在运行run()方法的时候都会new一个Sync新对象,所以synchronized(this)以及非static的synchronized方法,只能防止多个线程同时执行同一个对象的同步代码段。即synchronized锁住的是括号里的对象,而不是代码。对于非static的synchronized方法,锁的就是对象本身也就是this。
那怎么来解决上面这个问题呢?

锁同一个对象

class Sync{
    //锁的是当前对象this
    public synchronized void test(){
        System.out.println("test方法开始,当前线程为 "+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("test方法结束,当前线程为 "+Thread.currentThread().getName());
    }
}
class MyThread implements Runnable {
    private Sync sync;

    public MyThread(Sync sync){
        this.sync=sync;
    }

    @Override
    public void run() {
       sync.test();
    }
}
class Test{
    public static void main(String[] args)throws InterruptedException {
      MyThread myThread=new MyThread(new Sync());
       Thread thread1=new Thread(myThread);
        Thread thread2=new Thread(myThread);
        Thread thread3=new Thread(myThread);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

运行结果
在这里插入图片描述

全局锁(锁代码段)

全局锁有两种方式:使用类的静态同步方法和在代码块中锁当前Class对象。

使用类的静态同步方法
 synchronized与static一起使用,方法中无法使用this,所以它锁的不是this,而是类的Class对象,所以,static synchronized方法也相当于全局锁,相当于锁住了代码段。
class Sync{
    public static synchronized void test(){
        System.out.println("test方法开始,当前线程为 "+Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("test方法结束,当前线程为 "+Thread.currentThread().getName());
    }
}
class MyThread implements Runnable {

    @Override
    public void run() {
        Sync sync=new Sync();
       sync.test();
    }
}
class Test{
    public static void main(String[] args)throws InterruptedException {
      MyThread myThread=new MyThread();
       Thread thread1=new Thread(myThread);
        Thread thread2=new Thread(myThread);
        Thread thread3=new Thread(myThread);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
在代码块中锁当前Class对象
   synchronized (类名称.Class){},这种全局锁,锁的是类而不是this

class Sync{
    public  void test() {
    //锁的是类而不是this
        synchronized (Sync.class) {
            System.out.println("test方法开始,当前线程为 " + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("test方法结束,当前线程为 " + Thread.currentThread().getName());
        }
    }
}
class MyThread implements Runnable {

    @Override
    public void run() {
        Sync sync=new Sync();
       sync.test();
    }
}
class Test{
    public static void main(String[] args)throws InterruptedException {
      MyThread myThread=new MyThread();
       Thread thread1=new Thread(myThread);
        Thread thread2=new Thread(myThread);
        Thread thread3=new Thread(myThread);
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
发布了87 篇原创文章 · 获赞 73 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/HL_HLHL/article/details/84204295