Java多线程-----互斥与同步

本文介绍了Java中实现多线程互斥与同步的概念,强调了内部锁的作用,以及如何使用`synchronized`关键字创建互斥语句和互斥方法。通过实例展示了实例互斥方法和静态互斥方法,并通过售票问题探讨了多线程环境下资源的互斥访问策略,包括对票池的同步控制。
不同的线程在访问共享数据时,会因为交织进行而导致线程干扰和内存一致性错误。

每个对象都有一个内部锁与其对应。如果一个线程需要排他一致性访问对象的字段,它首先要在访问之前获得该对象的内部锁。当访问完成时需要释放该内部锁。线程在获得该锁和释放该锁期间称为拥有该锁。一旦线程拥有内部锁,其他任何线程都不能再获得该锁,它们在获得该锁时会被阻塞。
创建互斥性操作的方法是互斥语句。在Java中,实现互斥语句的关键字叫synchronized(同步),互斥语句的语法格式如下:

synchronized(lock){
   //critical code for accessing shared data.
   //...
}

这里lock是提供内部所的对象。这个语句是互斥代码的一般写法。另外往往整个方法需要进行互斥,这时就有所谓互斥方法。互斥方法根据方法类型的不同分为实例互斥方法和静态互斥方法。实例互斥方法的例子如下:

public synchronized void addName(String name){
      //Adding name to a shared list.
}

互斥实例方法实际获得的是当前实例对象的内部锁,前面的这个实例方法相当于下面写法的互斥语句:

public void addName(String name){
   synchronized(this){
        //Adding name to a shared list.
   }
}

静态互斥方法的例子如下:

Public class ClassA{
    public static synchronized void addName(String name){
        //Adding name to a shared list.
    }
}

静态互斥方法实际获得的是当前类Class对象的内部锁,前面这个静态方法相当于下面写法的互斥语句:

Public class ClassA{
    public static  void addName(String name){
        synchronized(this){
        //Adding name to a shared list.
        }
    }
}

举例:典型的售票问题。

public class ThreadDemo1 {
    public static void main(String[] args){
        Saler s1=new Saler("s-1");
        Saler s2=new Saler("s-2");
        s1.start();
        s2.start();
    }
}
class Saler extends Thread{
    static int tickets = 100;//因为取的是同100张票,所以设为静态变量
    //锁旗标
    static Object lock = new Object();//这里如果不把锁对象设置为static,那么Saler每创建一个对象,每个对象都有自己的锁,即每个线程参照的将是自己的锁,而不是共同的锁,程序执行会出现重复。
    private String name;
    public Saler(String name){
        this.name=name;
    }
    public void run(){
        while(true){
            int t=getTicket();
            if(t==0){
                return;
            }
            else{
                System.out.println(name+":"+t);
            }
        }
    }
    public int getTicket(){
        //互斥语句
        synchronized(lock){//lock作为锁旗标传入
            int t=tickets;
            try{
                Thread.sleep(50);
            }
            catch(Exception e){

            }
            tickets=tickets-1;
            return t>0 ? t : 0;
        }
    }
}

以上代码也可以改进为:

public class ThreadDemo2 {
    public static void main(String[] args){
        Object lock = new Object();//为了让两个线程访问同一把锁,先创建一把锁。将该锁对象分别作为参数传给每个线程
        Saler s1=new Saler("s-1",lock);
        Saler s2=new Saler("s-2",lock);
        s1.start();
        s2.start();
    }
}
class Saler extends Thread{
    static int tickets = 100;//因为取的是同100张票,所以设为静态变量
    //锁旗标
    Object lock ;//成员变量
    private String name;
    public Saler(String name,Object lock){//将该锁对象作为参数传入Saler()构造方法
        this.name=name;
        this.lock=lock;
    }
    public void run(){
        while(true){
            int t=getTicket();
            if(t==0){
                return;
            }
            else{
                System.out.println(name+":"+t);
            }
        }
    }
    public int getTicket(){
        //互斥语句
        synchronized(lock){       //lock作为锁旗标传入
            int t=tickets;
            try{
                Thread.sleep(50);
            }
            catch(Exception e){

            }
            tickets=tickets-1;
            return t>0 ? t : 0;
        }
    }
}

第三种写法:
引入票池,票池只有一个,以票池本身作为锁旗标,将取票的方法放入票池中
代码如下:

public class ThreadDemo6 {
    public static void main(String[] args){
        TicketPool pool=new TicketPool();
        Saler s1=new Saler("s-1",pool);
        Saler s2=new Saler("s-2",pool);
        s1.start();
        s2.start();
    }
}
class Saler extends Thread{

    //锁旗标
    Object lock ;//成员变量
    private String name;
    private TicketPool pool;
    public Saler(String name,TicketPool pool){//将该锁对象作为参数传入Saler()构造方法
        this.name=name;
        this.pool=pool;
    }
    public void run(){
        while(true){
            int t=pool.getTicket();
            if(t==0){
                return;
            }
            else{
                System.out.println(name+":"+t);
            }
        }
    }
}
class TicketPool{
    static int tickets = 100;//因为取的是同100张票,所以设为静态变量
    public int getTicket(){
        //互斥语句
        synchronized(this){//传入this,说明以当前实例对象(票池)作为锁旗标
            int t=tickets;
            try{
                Thread.sleep(50);
            }
            catch(Exception e){
            }
            tickets=tickets-1;
            return t>0 ? t : 0;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值