线程同步

线程同步

  • 当多个线程操作同一资源,例如上万人同时抢几张火车票,几个人都想上厕所但只有一个坑位。这个时候就会由于资源的抢夺,导致程序输出错误的结果,比如说,一个人还没上完厕所,另一个就进去了。
  • 线程同步就是用来解决上述问题,总的来说就是通过队列+锁的方式来控制对公共资源操作时不受其他线程影响
  • 通过加锁的方式保证了程序有条不紊的运行,但是一个线程持有锁会导致其他所有线程被挂起,加锁解锁导致了比较多的上下文切换和调度延时,因此牺牲了一定的效率
  • 在Java中是通过锁机制synchronized来实现的,主要可以分为两种
  1. 同步方法 public synchronized void method(int args){}

  2. 同步块 synchronized(Obj){}

代码演示

例1-同步方法

package MultiProcess;

public class Unsafe {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket, "我").start();
        new Thread(buyTicket, "我室友").start();
        new Thread(buyTicket, "黄牛").start();
    }
}

class BuyTicket implements Runnable{
    boolean flag = true;
    private int ticketNums = 10;

    @Override
    public void run() {
        while(flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void buy() throws InterruptedException{
        if(ticketNums <= 0){
            flag = false;
            return;
        }
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + "买了第" + ticketNums-- + "张票");
    }
}
结果
我买了第4张票
我室友买了第3张票
黄牛买了第2张票
我买了第1张票
黄牛买了第-1张票
我室友买了第0张票

由于多线程操作同一资源ticketNum,进行ticketNum--,导致了线程不安全问题。因为ticketNum--操作需要经过三个步骤,把ticketNum的值读到寄存器中,进行-1运算,写回内存。所以A线程刚把ticketNum放到寄存器,B线程就会读到寄存器里的值,没有读-1运算后的值,导致了结果的异常。

添加同步方法
package MultiProcess;

public class Unsafe {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket, "我").start();
        new Thread(buyTicket, "我室友").start();
        new Thread(buyTicket, "黄牛").start();
    }
}

class BuyTicket implements Runnable{
    boolean flag = true;
    private int ticketNums = 10;

    @Override
    public void run() {
        while(flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void buy() throws InterruptedException{
        if(ticketNums <= 0){
            flag = false;
            return;
        }
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName() + "买了第" + ticketNums-- + "张票");
    }
}
结果
黄牛买了第7张票
我买了第6张票
黄牛买了第5张票
黄牛买了第4张票
我室友买了第3张票
黄牛买了第2张票
黄牛买了第1张票

例2-同步块

package MultiProcess;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
    public static void main (String[] args) {
        List<String> list = new ArrayList<String>();
        for(int i = 0; i < 100000; i++){
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

结果
99983

因为多个线程可能将元素放到了同一位置上

添加同步块
package MultiProcess;
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
    public static void main (String[] args) {
        List<String> list = new ArrayList<String>();
        for(int i = 0; i < 100000; i++){
            new Thread(() -> {
                synchronized (list) {
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}
结果
100000
posted @ 2020-10-18 19:49  提笔书几行  阅读(60)  评论(0编辑  收藏  举报