栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

线程安全之同步

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

线程安全之同步

线程安全问题 0,线程安全问题举例

当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。

多窗口卖票问题
public class RunnableImpl implements Runnable{
    private int ticket=100;
    @Override
    public void run(){
        while(true){
            if(ticket>0){
                try{
                    Thread.sleep(10);
                }catch(InterruptException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在卖第"+
                                  ticket+"张票");
                ticket--;
            }
        }
    }
}
public class Demo01Ticket{
    public static void main(String[] args){
        //创建实现类对象
        RunnableImpl run=new RunnableImpl();
        //同一个任务多个线程执行
        Thread t0=new Thread(run);
        Thread t1=new Thread(run);
        Thread t2=new Thread(run);
        //启动
        t0.start();
        t1.start();
        t2.start();
    }
}

要解决上述多线程并发访问一一个资源的安全性问题:也就是解决重复票与不存在票问题, Java中提供了同步机制(synchronized)来解决。

Java中可以通过3种方式完成同步操作:
  • 1,同步代码块
  • 2,同步方法
  • 3,锁机制
1,同步代码块 1.1 同步代码块

synchronized关键字可以用于方法中的某个区块中,表示对这个区块中的资源进行互斥的访问。

synchronized(同步锁对象){
    需要同步操作的代码;
}
1.2 同步锁

一个抽象概念,可以想象为在对象上标记了一个锁

  • 1,锁对象,可以是任意对象
  • 2,多个线程对象要使用同意把锁

注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着(BLOCKED阻塞)。

public class RunnableImpl implements Runnable{
    private int ticket=100;
    //创建一个锁对象
    Object obj=new Object();//------------锁对象只能创建在run方法之外,保证锁对象唯一
    @Override
    public void run(){
        while(true){
            //同步代码块
        synchronized(obj){
        	if(ticket>0){
                try{
                    Thread.sleep(10);
                }catch(InterruptException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在卖										第"+ticket+"张票");
                ticket--;
            	}
            }
        }
    }
}
总结

同步中的线程没有执行完毕不会释放锁,同步外的线程没有锁进不去同步。

2,同步方法 步骤:
  • 1,把访问了共享数据的代码抽取出来,放到一个方法中
  • 2,在方法上添加synchronized修饰符

该方法本质上就是同步代码块,其锁对象即为new RunnableImpl(),也就是this。

public class RunnableImpl implements Runnable{
    private int ticket=100;
    @Override
    public void run(){
        while(true){
            sellTicket();
        }
    }
    public synchronized void sellTicket(){//-------------同步关键字
        if(ticket>0){
            try{    
                Thread.sleep(10);
                }catch(InterruptException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在卖第"+
                                  ticket+"张票");
                ticket--;
     
    }
}
静态的同步方法:

静态的同步方法也能实现同步,但由于是静态的,所以锁对象不可能再是this对象。

静态同步方法的锁对象是本类的class属性,即class文件对象,字节码文件

synchronized(Runnable.class){
    要同步的代码;
}
public class RunnableImpl implements Runnable{
    private Static int ticket=100;//------------------静态同步方法只能访问静态变量
    @Override
    public void run(){
        while(true){
            sellTicket();
        }
    }
    public Static synchronized void sellTicketStatic(){//-------------同步关键字
        if(ticket>0){
            try{    
                Thread.sleep(10);
                }catch(InterruptException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在卖第"+
                                  ticket+"张票");
                ticket--;
     
    }
}
3,Lock锁(接口)

jdk 1.5之后

java.util.concurrent.locks.Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

Lock接口中的方法:lock(),unlock()

java. util. concurrent. Locks. ReentrantLock实现了 Lock接口

步骤:
  • 1,在成员位置创建一个ReentrantLock对象;
  • 2,在可能出现线程安全问题的代码前调用Lock接口中的方法lock()获取锁。
  • 3,在其后使用unlock()方法释放锁;
public class RunnableImpl implements Runnable{
    private int ticket=100;
    //1,在成员位置创建一个ReentrantLock对象;
    Lock lo=new ReentrantLock();
    @Override
    public void run(){
        while(true){
            //2,在可能出现线程安全问题的代码前调用Lock接口中的方法lock()获取锁。
            lo.lock();
            if(ticket>0){
                try{
                    Thread.sleep(10);
                }catch(InterruptException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在卖第"+
                                  ticket+"张票");
                ticket--;
            }
            //3,在其后使用unlock()方法释放锁;
            lo.unlock();
        }
    }
}

优化后:保证最后能够释放锁对象finally

public class RunnableImpl implements Runnable{
    private int ticket=100;
    //1,在成员位置创建一个ReentrantLock对象;
    Lock lo=new ReentrantLock();
    @Override
    public void run(){
        while(true){
            //2,在可能出现线程安全问题的代码前调用Lock接口中的方法lock()获取锁。
            lo.lock();
            if(ticket>0){
                try{
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName()+"正在卖						第"+ ticket+"张票");
                	ticket--;
                }catch(InterruptException e){
                    e.printStackTrace();
                }finally{
                    //保证最后能够释放锁对象
                    lo.unlock();
                }
                
            }
            //3,在其后使用unlock()方法释放锁;
            lo.unlock();
        }
    }
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/658058.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号