线程的同步问题

并发:一个对象被多个线程同时操作

 

处理并发问题:

线程同步=队列+锁


最天然的处理:让线程排队,一个一个来(队列)

线程同步是一个等待机制,多个线程要同时操作一个对象时,让线程进入对象等待池,形成队列,一个一个来

 

锁:在一个线程正在操作该对象时,要上锁,防止其他线程抢占操作(每个对象都有一把锁)
synchronized(锁机制)
当一个线程获得对象的排它锁,独占资源,其他资源必须等待,使用后释放锁即可
会存在的问题:
1.会引发性能问题
2.如果一个优先级高的线程在等待一个优先级低的线程释放锁,会引发优先级倒置,发生性能问题。


每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

线程是不安全的,必须加以控制(队列+锁)

线程未加锁:

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 < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }

        System.out.println(list.size());
    }
}

运行结果为:

 

 

 注意:结果是变化的,可能有多个线程抢占同一位置(并发问题)

线程加锁后:

package syn;

import java.util.ArrayList;
import java.util.List;

public class SafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
            for (int i = 0; i < 10000; i++) {
                new Thread(()->{
                    synchronized (list){
                        list.add(Thread.currentThread().getName());
                    }
                }).start();
            }
        Thread.sleep(1000);
        System.out.println(list.size());

    }
}

运行结果为:

 

 

 

 

 

线程同步的解决方法:


1.隐式定义同步锁
synchronized 关键字加在 方法上(加在public与返回值之间)---》默认锁this对象,也就是该方法所在的类的对象
或synchronized(obj){}代码块(用{}将整个代码块包含进去)--》锁的是(obj)中obj对象

未加锁:

//不安全的银行
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(100,"结婚基金");
        Drawing man = new Drawing(account,50,"男人");
        Drawing woman = new Drawing(account,100,"女人");

        man.start();
        woman.start();
    }

}


//个人账户
class Account{
    private int money;//账户余额
    private String name;//账号名称

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


//银行,模拟取票
class Drawing extends Thread{
     private Account account;//账户
     private int drawMoney;//取了多少钱
     private int nowMoney;//现在手里多少钱

    public Drawing(Account account,int drawMoney,String name){
        super(name);
        this.account = account;
        this.drawMoney = drawMoney;
    }

    @Override
    public void run() {
        //判断有没有钱
        if(account.getMoney() - drawMoney <0){
            System.out.println(account.getName()+"卡里的钱不够,取不了这么多!");
            return;
        }
        try {
            Thread.sleep(1000);//模拟一个延时
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.setMoney(account.getMoney()-drawMoney);
        nowMoney = nowMoney + drawMoney;
        System.out.println(account.getName()+"卡里的余额为"+account.getMoney());
        System.out.println(Thread.currentThread().getName()+"现在手上有"+nowMoney+"钱!");
    }
}

加锁后

public class SafeBank {
    public static void main(String[] args) {
        Account1 account1 = new Account1(100,"结婚基金");
        Drawing1 man = new Drawing1(account1,50,"男人");
        Drawing1 woman = new Drawing1(account1,100,"女人");

        man.start();
        woman.start();
    }
}

//个人账户
class Account1{
    private int money;//账户余额
    private String name;//账号名称

    public Account1(int money, String name) {
        this.money = money;
        this.name = name;
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


//银行,模拟取票
class Drawing1 extends Thread{
    private Account1 account1;//账户
    private int drawMoney;//取了多少钱
    private int nowMoney;//现在手里多少钱

    public Drawing1(Account1 account1,int drawMoney,String name){
        super(name);
        this.account1 = account1;
        this.drawMoney = drawMoney;
    }

    @Override
    public void run() {
        synchronized (account1){//加对象锁代码块,解决线程同步
            //判断有没有钱
            if(account1.getMoney() - drawMoney <0){
                System.out.println(account1.getName()+"卡里的钱不够,取不了这么多!");
                return;
            }
            try {
                Thread.sleep(1000);//模拟一个延时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account1.setMoney(account1.getMoney()-drawMoney);
            nowMoney = nowMoney + drawMoney;
            System.out.println(account1.getName()+"卡里的余额为"+account1.getMoney());
            System.out.println(Thread.currentThread().getName()+"现在手上有"+nowMoney+"钱!");
        }
    }
}

 

注意:
1.锁的对象是公共资源,是变化的量,是需要增删改查的对象
2.synchronized十分消耗资源,运行缓慢(可能主线程运行完了,用的锁资源的线程还没运行完,得sleep等待一下才行),{ }中包含的代码
越少,程序运行起来就越快。(在例子中加sleep是为了模拟一个延时,让所有线程都可以抢占同一资源)


死锁:多个线程抱着对方需要的资源不放,形成僵持

避免方法:synchronized{}中不要包含有另一个synchronized{}公共资源,要让资源等待相互独立。

造成死锁:

package syn;

public class DeadLock {
    public static void main(String[] args) {
        MakeUp g1 = new MakeUp(0,"灰姑娘");
        MakeUp g2 = new MakeUp(1,"白雪公主");

        g1.start();
        g2.start();
    }
}
//口红
class Lipstick{

}

//镜子
class Mirror{

}

class MakeUp extends Thread{
//    需要的资源只有一份,用static
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
//    选择
    int choice;
    String name;//化妆人的名字

    MakeUp(int choice,String name){
        this.choice = choice;
        this.name = name;
    }

    @Override
    public void run() {
//        化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
//    化妆方法
    public void makeup() throws InterruptedException {
        if (choice == 0){
//            先获得口红,再获得镜子
            synchronized (lipstick){
                System.out.println(this.name+"获得了口红");
                Thread.sleep(1000);

                //                等一秒钟,获得镜子
                synchronized (mirror){
                    System.out.println(this.name+"获得了镜子");
                }
            }


        }else {
            synchronized (mirror){
//                先获得镜子,再获得口红
                System.out.println(this.name+"获得了镜子");
                Thread.sleep(1000);

                synchronized (lipstick){
                    System.out.println(this.name+"获得了口红");
                }

            }

        }

    }
}

运行结果为:

 

 

 程序还在运行,没有停止

 

解决死锁:

package syn;

public class DeadLock {
    public static void main(String[] args) {
        MakeUp g1 = new MakeUp(0,"灰姑娘");
        MakeUp g2 = new MakeUp(1,"白雪公主");

        g1.start();
        g2.start();
    }
}
//口红
class Lipstick{

}

//镜子
class Mirror{

}

class MakeUp extends Thread{
//    需要的资源只有一份,用static
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
//    选择
    int choice;
    String name;//化妆人的名字

    MakeUp(int choice,String name){
        this.choice = choice;
        this.name = name;
    }

    @Override
    public void run() {
//        化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
//    化妆方法
    public void makeup() throws InterruptedException {
        if (choice == 0){
//            先获得口红,再获得镜子
            synchronized (lipstick){
                System.out.println(this.name+"获得了口红");
                Thread.sleep(1000);
            }
            //                等一秒钟,获得镜子
            synchronized (mirror){
                System.out.println(this.name+"获得了镜子");
            }


        }else {
            synchronized (mirror){
//                先获得镜子,再获得口红
                System.out.println(this.name+"获得了镜子");
                Thread.sleep(1000);
            }
            synchronized (lipstick){
                System.out.println(this.name+"获得了口红");
            }

        }

    }
}

运行结果为:

 

 

 运行完成

 

2.显示定义同步锁(Lock接口)
jdk5.0后,可通过显示定义同步锁对象Lock对象,来实现线程同步
ReentrantLock实现了Lock接口,可用于解决线程并发问题
class A{
// 定义lock锁
private final ReentrantLock lock = new ReentrantLock();

try {
lock.lock();//加锁

//要加锁的同步代码块

}finally {
lock.unlock();//解锁,如果同步代码块有异常要抛出,要将unlock()写在finally{}内
}


}

 

package syn;

import java.util.concurrent.locks.ReentrantLock;

//可重入锁的测试
public class TestReentrantLock {
    public static void main(String[] args) {
        Ticket t = new Ticket();
        new Thread(t,"A").start();
        new Thread(t,"B").start();
        new Thread(t,"C").start();
    }
}


class Ticket implements Runnable{
    private int num = 10;
//    定义lock锁
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            try {
                lock.lock();//加锁

                if(num >0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"买了票----"+num--);
                }else {
                    break;
                }

            }finally {
                lock.unlock();//解锁
            }

        }
    }
}

 

synchronized与Lock的比较
1.Lock是显式锁,要手动开启,关闭;synchronized是隐式锁,出了作用域会自动关闭
2.Lock只有代码块锁,synchronized有代码块锁和方法锁
3.Lock锁,jvm调用时间更少,性能更好,有更好的扩展性
4.优先使用顺序
Lock>同步代码块(已经进入了方法体,分配相应的资源)>同步方法(在方法体外部)

 

posted @ 2020-07-23 18:11  DannyBoy~  阅读(209)  评论(0编辑  收藏  举报