java并发编程一一多线程线程安全(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34846877/article/details/81505222

1.什么是线程安全?

1.1为什么有线程安全问题?

当多个线程同时共享同一个全局变脸或静态变量,做写的操作时,可能会发生数据冲突的问题,
也就是线程安全的问题。但是做读操作是不会发生数据冲突问题。
举例:现在有100张火车票,有两个窗口同时抢火车票,用多线程模拟抢票效果。

public class ThreadTrain implements Runnable {
    private int trainCount = 100;

    @Override
    public void run() {
        while (trainCount > 0) {
            try {
                Thread.sleep(50);
            } catch (Exception e) {

            }
            sale();
        }
    }

    public void sale() {
        if (trainCount > 0) {
            System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
            trainCount--;
        }
    }

    public static void main(String[] args) {
        ThreadTrain threadTrain = new ThreadTrain();
        Thread t1 = new Thread(threadTrain, "①号");
        Thread t2 = new Thread(threadTrain, "②号");
        t1.start();
        t2.start();
    }
}

运行结果:一号窗口和二号窗口同时出售的火车票会出现重复的票数。
结论:多个线程共享同一个全局成员变量时,做写的操作可能会发生数据冲突问题

2.线程安全的解决办法

1 如何解决多线程之间线程安全问题?
使用多线程之间同步synchronized或使用锁(lock)
2 为什么使用线程同步或使用锁能解决线程安全问题呢?
将可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程执行。
代码执行完成后释放资源,然后才能让其它线程进行执行。这样就可以解决线程
不安全的问题。
3 什么是多线程之间同步?
当多个线程共享同一个资源,不会受到其它线程的干扰。
4 什么是多线同步?
当多个线程共享同一个资源,不会受到其它线程的干扰。

2.1内置的锁

java提供了一种内置的锁机制来支持原子性。
每个java对象都可以作一个实现同步的锁,成为内置锁。线程进入同步代码之前自动获取到锁
代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁。
内置锁为互斥锁,即线程A获取到锁后,线程B阻塞知道线程A释放锁,线程B才能获取到同一个锁。
内置锁使用synchronized关键字实现,synchronized关键字有两种用法:
1. 修饰需要进行同步的方法(所有访问状态变量的方法都必须进行同步),此时充当锁的对象调用同步方法的对象
2. 同步代码块和直接使用synconized 修饰需要同步的方法是一样的,但是锁的粒度可以更细,并且
充当锁的对象不一定是this,也可以是其它对象,所以使用起来更加灵活。

2.2同步代码块synchronized

就是将可能会发生线程安全问题的代码给括起来。

   synchronized(同一个数据){
       //可能要发生线程冲突的代码
   }
   就是同步代码块
   synchronized(对象){//这个对象可以为任意对象
        //需要被同步的代码
   }

对象如同锁,持有锁的线程可以在同步中执行
没有持有锁的线程即使获取CPU的执行权,也进不去
同步的前提:
1. 必须要有两个或者两个以上的线程
2. 必须是多个线程使用同一个锁
必须保证同步中只能有一个线程在运行
好处:解决多线程的安全问题
弊端:多个线程需要判断锁,较为消耗资源、抢锁的资源。

2.3同步方法

2.3.1什么是同步方法呢?

就是在方法上修饰synchronized 成为同步方法。
代码示例:

public synchronized void sale() {
        if (trainCount > 0) {
            System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
            trainCount--;
        }
    }

2.3.2同步方法使用的是什么锁呢?

同步方法使用的this锁
证明方式:一个线程使用同步代码块(this 明锁),另一个线程使用同步函数。如果两个线程抢票不能实现同步,
那么就会出现数据错误。
代码示例:

class Thread009 implements Runnable {
    private int trainCount = 100;
    private Object oj = new Object();
    public boolean flag = true;

    public void run() {
        if (flag) {
            while (trainCount > 0) {
                synchronized (this) {
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                    if (trainCount > 0) {
                        System.out
                                .println(Thread.currentThread().getName() + "," + "出售第" + (100 - trainCount + 1) + "票");
                        trainCount--;
                    }
                }
            }
        } else {
            while (trainCount > 0) {
                sale();
            }
        }
    }

    public synchronized void sale() {
        try {
            Thread.sleep(10);
        } catch (Exception e) {
            // TODO: handle exception
        }
        if (trainCount > 0) {
            System.out.println(Thread.currentThread().getName() + "," + "出售第" + (100 - trainCount + 1) + "票");
            trainCount--;
        }
    }
}

public class Test009 {
    public static void main(String[] args) throws InterruptedException {
        Thread009 threadTrain = new Thread009();
        Thread t1 = new Thread(threadTrain, "窗口1");
        Thread t2 = new Thread(threadTrain, "窗口2");
        t1.start();
        Thread.sleep(40);
        threadTrain.flag = false;
        t2.start();
    }
}

2.3.3静态同步函数

什么是静态同步函数?
方法上加上static关键字,使用synchornized关键字修饰,或使用类.class文件。
静态的同步函数使用的锁是 –>该函数所属字节码文件对象
可以使用 getClass方法获取,也可以使用当前类名.class表示。
代码示例:

public static void sale() {
    synchronized (ThreadTrain3.class) {
        if (trainCount > 0) {
            System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainCount + 1) + "张票");
            trainCount--;
        }
    }
}

总结:
synchronized修饰方法使用的锁是 当前的 this锁。
synchronized修饰静态方法使用的锁是当前类的字节码文件。

猜你喜欢

转载自blog.csdn.net/qq_34846877/article/details/81505222