线程的同步(解决多线程安全问题)

Posted 巧克力爱王子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程的同步(解决多线程安全问题)相关的知识,希望对你有一定的参考价值。

多线程出现安全问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有   执行完,另一个线程参与进来执行。导致共享数据的错误。   解决方法:   对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以   参与执行。   Java对多线程的安全问题提供了专门的解决方法:同步机制。   方法一:同步代码块   synchronized     //需要被同步的代码   说明:   1、操作共享数据的代码,即为需要被同步的代码(注意不要包含多了也不要包含少了);   2、共享数据:多个线程共同操作的变量;   3、同步监视器:俗称锁,任何一个类的对象都可以充当锁(要求各线程必须共用一把锁);   4、在实现Runnab接口创建多线程的方式中,可以考虑使用this作为同步监视器,在继承Thread类   创建多线程的方式中,可以考虑 类.class作为同步监视器。   下面是一个简单的多窗口卖票问题  
class Window implements Runnable//实现Runnable方法创建多线程

    private static int ticket = 100;//票数

    @Override
    public void run() //重写run方法
        while (true)
          synchronized (this) //将操作共享数据的代码用synchronized包起来
              if (ticket > 0) 
                  //当前线程的名字
                  System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);

                  ticket--;
               else 
                  break;
              
          
        
    

此时,对于操作共享数据的语句,只有当一个线程执行完了,释放了锁,其他线程才能进入。

方法二:使用同步方法

当操作共享数据的代码完全完整的声明在一个方法中,可以将此方法设置为同步的。

注意:

1、同步方法仍然涉及到同步监视器,只是不需要我们显示的声明;

2、非静态的同步方法,同步监视器为 this

      静态的同步方法,同步监视器为当前类本身(类.class)

此为非静态的同步方法

//实现Runnable接口创建多线程
class Window1 implements Runnable

    private static int ticket = 100;//票数

    @Override
    public void run() //重写run方法
        while (true)
            show();//该方法为操作共享数据的方法
        
    

    private synchronized void  show()//同步监视器为this

            if (ticket > 0) 

                System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);

                ticket--;
            

        

 

此为静态的同步方法

//继承Thread类来创建多线程
class Window2 extends Thread 

    private static int ticket = 100;//票数

    @Override
    public void run() 
        while (true)
            show();//该方法为操作共享数据的方法
        
    

    private  static synchronized void  show()//同步监视器为Window2.class

        if (ticket > 0) 

            System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket);

            ticket--;
        
    

 

方式三:lock(锁)

class A private final ReentrantLock lock = new ReenTrantLock(); public void m() lock.lock(); try // 保证线程安全的代码 ; finally lock.unlock();   说明:   1、从 JDK 5.0 开始, Java 提供了更强大的线程同步机制 —— 通过显式定义同步锁对象来实现同   步。同步锁使用Lock 对象充当;   2、 java.util.concurrent.locks.Lock 接口是控制多个线程对共享资源进行访问的 工具。锁提供了对共   享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得   Lock对象;   3、ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,在实现线   程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
class Windowlock implements Runnable

    private  int trick = 100;
//    实例化ReentrantLock
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() 

        while (true) 
            try 
                  //调用锁定方法
                    lock.lock();
                    if(trick > 0)

                        System.out.println(Thread.currentThread().getName() + ":" + trick);
                        trick--;
                    else
                        break;
                    
             finally 
//解锁
                lock.unlock();
            
        

    

 

synchronized 与 lock的对比:   1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁), synchronized 是隐式锁,出了作用域自动   释放;   2. Lock只有代码块锁, synchronized 有代码块锁和方法锁;   3. 使用Lock 锁, JVM 将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更   多的子类)   优先使用顺序:   lock——>同步代码块——>同步方法

以上是关于线程的同步(解决多线程安全问题)的主要内容,如果未能解决你的问题,请参考以下文章

JAVA多线程安全

多线程题

线程安全问题

线程安全问题

JAVA基础——多线程

线程的同步