乖乖努力!!!

导航

多线程中的安全问题

synchronized

synchronized格式

  1. 同步代码块的格式:
    synchronized (锁对象){...}
  2. 非静态同步方法的格式:
    public synchronized 方法返回值 方法名称 (){....}
  3. 静态同步方法的格式:
    public static synchronized 方法返回值 方法名称(){....}

synchronized的同步代码块

1.使用同步代码块解决多线程安全问题

***使用同步代码块实现多线程卖票***
使用继承Thread实现多窗口卖票
Thread实现类:
//使用继承Thread类实现卖票
public class MyThread extends Thread{
    public static int ticket = 20;
    public static Object obj = new Object();//设置锁对象

    public MyThread(String name) {
        super(name);
    }
    public void run(){
        while (true){
            synchronized (obj){
//                synchronized (MyThread.class){
            if (ticket <= 0){
                System.out.println("票卖完了");
                break;
            }else{
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"张票");
                ticket--;
            }
          }
        }
    }
}
测试类:
public class Test01 {
    public static void main(String[] args) {
        MyThread myThread1 = new MyThread("李哈哈");
        MyThread myThread2 = new MyThread("李嘿嘿");
        myThread1.start();
        myThread2.start();
    }
}

2.使用同步代码块解决多个入口执行共享资源问题

多个入口操作共享资源,实现同步代码块多窗口卖票
Thread实现类:
//实现多个同步代码块进行多入口操作共享资源
public class MyThread extends Thread{
    public static int ticket = 60;

    public MyThread(String name) {
        super(name);
    }
    public void run(){
        while (true){
            if (ticket == 0){
                break;
            }
            if (ticket % 2 == 0){
                synchronized (this){//必须保证和所有的同步代码块的锁对象相同
                    if (ticket > 0){
                        System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"票");
                        ticket--;
                    }
                }
            }else{
                synchronized (this){//必须保证和所有的同步代码块的锁对象相同
                    if (ticket > 0){
                        System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"票");
                        ticket--;
                    }
                }
            }
        }
    }
}
测试类:
public class Test01 {
    public static void main(String[] args) {
        MyThread myThread1 = new MyThread("李哈哈");
        MyThread myThread2 = new MyThread("李嘿嘿");
        myThread1.start();
        myThread2.start();
    }
}

synchronized的非静态同步方法

1.使用非静态同步方法解决多线程安全问题

***使用静态同步方法***
使用接口Runnable和非静态同步方法多窗口卖票
Runnable接口的实现类:
public class MyRunnable implements Runnable{
    public static int num = 20;
    @Override
    public void run() {
        while (true){
            if (num == 0){
                break;
            }else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                method();
            }
        }

    }
    public synchronized void method(){//锁的对象默认是this
        if (num > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
            num--;
        }
    }
}

测试类:
public class Test01 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable, "窗口1");
        Thread t2 = new Thread(myRunnable, "窗口2");
        t1.start();
        t2.start();
    }
}

2.使用非静态同步方法解决多个入口执行共享资源问题

多个入口操作共享资源,实现非静态同步方法多窗口卖票
//使用非静态实现多入口多窗口卖票
public class MyRunnable implements Runnable{
    public static int num = 20;
    @Override
    public void run() {
        while (true){
            if (num == 0){
                break;
            }else if (num % 2 == 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                method1();
            }else {
                method();
            }
        }

    }
    public synchronized void method(){//锁的对象默认是this
        if (num > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
            num--;
        }
    }
    public synchronized void method1(){//锁的对象默认是this
        if (num > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
            num--;
        }
    }
}
测试类:
public class Test01 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable, "窗口1");
        Thread t2 = new Thread(myRunnable, "窗口2");
        t1.start();
        t2.start();
    }
}

synchronized的静态同步方法

1.使用静态同步方法解决多线程安全问题
使用静态同步方法

使用Runnable接口和静态同步方法实现多窗口卖票
//使用静态同步方法实现多窗口卖票
public class MyRunnable implements Runnable {
    public static int ticket = 40;
    @Override
    public void run() {
        while (true){
            if (ticket == 0){
                break;
            }else{
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                method();
            }
        }
    }
    public static synchronized void method(){
        if (ticket > 0){
            System.out.println(Thread.currentThread().getName()+"卖了"+ticket+"张票");
            ticket--;
        }
    }
}

测试类:
public class Test01 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable, "小姐姐");
        Thread t2 = new Thread(myRunnable, "大姐姐");
        t1.start();
        t2.start();
    }
}

2.使用非静态同步方法解决多个入口执行共享资源问题

使用多个静态同步方法实现多卤肉执行共享资源
//使用静态同步方法实现多入口多窗口卖票
public class MyRunnable implements Runnable{
    public static int num = 20;
    @Override
    public void run() {
        while (true){
            if (num == 0){
                break;
            }else if (num % 2 == 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                method1();
            }else {
                method();
            }
        }

    }
    public static synchronized void method(){//锁的对象默认是当前类的CLass对象
        if (num > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
            num--;
        }
    }
    public static synchronized void method1(){//锁的对象默认是当前类的CLass对象
        if (num > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+num+"票");
            num--;
        }
    }
}
测试类:
public class Test01 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable, "a");
        Thread t2 = new Thread(myRunnable, "b");
        t1.start();
        t2.start();
    }
}

多入口和多窗口卖票的不同情况

多入口操作共享资源时,使用同步代码块和静态同步方法,非静态同步方法的搭配使用,实现多窗口卖票

多入口的操作共享资源的多种情况:

  1. 多个入口都是同步代码块时:需要保持多个同步代码块对的锁对象是同一个。
  2. 多个入口有同步代码块和非静态同步方法时:需要将同步代码块中的锁对象修改为this,保证同步代码块和非静态同步方法的锁对象一致
  3. 多个入口有同步代码块和静态同步方法时:需要将同步代码块中的锁对象修改为当前类的Class对象,有两种方式修改同步代码块中的锁改为Class对象(前类名.class或者this.getClass),这样保证了同步代码块和静态同步方法的锁对象一致。

代码实现

1.同步代码块+非静态同步方法+多入口操作共同资源
思路:
需要使得同步代码块中的锁对象和非静态同步方法中的锁对象相同,但是非静态方法中的锁默认为this,当前类,所以只有修改同步代码块中的锁对象也为this,才能保证两者锁对象相同,多个入口的锁应该使用相同的锁,所以使用同一种非静态同步方法即可。
代码块:

同步代码块+非静态同步方法
//使用同步方法解决多线程安全问题
//如果有多个入口可以操作共享的资源
//可以同时使用同步代码块和同步方法
public class MyRunnable implements Runnable{
    public static int ticket = 40;
    @Override
    public void run() {
        while (true){
            if (ticket % 3 == 0){
//            使用同步代码块
                synchronized (this){
                    if (ticket > 0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
                        ticket--;
                    }
                }
            }else{
//                使用同步方法
                method();
            }
        }
    }
    public synchronized void method(){//同步方法的锁对象默认是this
        if (ticket > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
            ticket--;
        }
    }
}

2.同步代码块+静态同步方法+多入口操作共同资源

思路:
需要同步代码块中的锁对象和静态同步方法的锁对象保持一致,静态同步方法中的锁对象默认为当前类的Class对象,所以修改同步代码块中的锁对象使得两者保持一致即可。
代码块:

同步代码块+静态同步方法+多入口操作共享资源
//使用同步静态方法解决多线程安全问题
//如果有多个入口可以操作共享的资源
//可以同时使用同步代码块和静态同步方法
public class MyRunnable implements Runnable{
    public static int ticket = 40;
    @Override
    public void run() {
        while (true){
            if (ticket % 3 == 0){
//            使用同步代码块
//                synchronized (MyRunnable.class){
                    synchronized (this.getClass()){
                    if (ticket > 0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
                        ticket--;
                    }
                }
            }else{
//                使用静态同步方法
                method();
            }
        }
    }
    public static synchronized void method(){//静态同步方法的锁对象默认是当前类的class对象
        if (ticket > 0){
            System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"号票");
            ticket--;
        }
    }
}

Lock锁

使用Lock实现多线程卖票:

Lock解决多线程安全问题
//使用锁实现多线程卖票
public class MyRunnable implements Runnable{
    private static int ticket = 100;
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            if (ticket == 0){
                System.out.println("票卖完了");
                break;
            }else {
                lock.lock();
                if (ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"在卖"+ticket+"票");
                    ticket--;
                }
            }
            lock.unlock();
        }
    }
}

测试类:
public class Test01 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable,"窗口1");
        Thread t2 = new Thread(myRunnable,"窗口2");
        Thread t3 = new Thread(myRunnable,"窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

posted on 2022-08-19 13:46  乖乖在努力  阅读(35)  评论(0编辑  收藏  举报