Java基础教程——线程同步

Posted tigerlion

tags:

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

线程同步

synchronized:同步的

例:取钱

不做线程同步的场合,假设骗子和户主同时取钱,可能出现这种情况:

  • 【骗子】取款2000:账户余额1000
  • 【户主】取款2000:账户余额1000
  • 结果是社会财富增加1000,银行不开心。

代码如下所示:

// 账户类
class Account 
    private int accountBalance = 2000;
    public void withdraw(String userName, int amount) 
        System.out.println(userName + "===in===");
        if (accountBalance >= amount) 
            try 
                Thread.sleep(500);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            accountBalance -= amount;// 取钱
            System.out.println(userName + "取款" + amount + ",余额:" + accountBalance);
         else 
            System.out.println(userName + "企图取款" + amount + ",但余额只有:" + accountBalance);
        
        System.out.println(userName + "===out===");
    

class MyThread extends Thread 
    @Override
    public void run() 
        取钱Demo.act.withdraw(getName(), 1000);
    

public class 取钱Demo 
    static Account act = new Account();// 账户就一份
    public static void main(String[] args) 
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        t1.setName("户主");
        t2.setName("骗子");
        t2.start();
        t1.start();
    

想要银行开心,就需要对线程进行同步处理,避免出现重复取款的情况。


线程同步

方法一:锁对象

    public void withdraw(String userName, int amount) 
        synchronized (this) 
          ……
        
    

如果是静态方法,没有this,则是锁住【类名.class】

    public static void withdraw(String userName, int amount) 
        synchronized (Account.class) 
          ……
        
    

方法二:锁方法

当方法被调用时,调用线程必须获得当前对象的锁,否则将等待下去。
方法结束后,锁会被释放。

public synchronized void withdraw(String userName, int amount) ...

方法三:ReentrantLock重入锁

ReentrantLock是java.util.concurrent.locks.Lock接口的一个实现类。(reentrant:[r?‘entr?nt]再进去)

一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大(可以中断、定时)。

API文档上建议的用法:

建议总是 立即实践,使用 lock 块来调用 try,在之前/之后的构造中,最典型的代码如下: 

 class X 
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m()  
     lock.lock();  // block until condition holds
     try 
       // ... method body
      finally 
       lock.unlock()
     
   
 

应用示例:

import java.util.concurrent.locks.ReentrantLock;
// 账户类
class Account 
    private int accountBalance = 2000;
    private ReentrantLock lock = new ReentrantLock();
    public void withdraw(String userName, int amount) 
        synchronized (Account.class) 
            lock.lock();
            try 
                System.out.println(userName + "===in===");
                if (accountBalance >= amount) 
                    Thread.sleep(500);
                    accountBalance -= amount;// 取钱
                    System.out.println(userName + "取款" + amount + ",余额:" + accountBalance);
                 else 
                    System.out.println(userName + "企图取款" + amount + ",但余额只有:" + accountBalance);
                
                System.out.println(userName + "===out===");
             catch (InterruptedException e) 
                e.printStackTrace();
             finally 
                lock.unlock();
            
        
    

练习:买票

(未做线程同步,请实现线程同步)

import java.util.Random;
public class 卖票 
    public static void main(String[] args) 
        // 一个Runnable实例对象
        SellTicket st = new SellTicket();
        // 创建三个线程对象
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");
        // 启动线程
        t1.start();
        t2.start();
        t3.start();
    

class SellTicket implements Runnable 
    // 定义票数
    private int tickets = 100;
    private void sell() 
        if (tickets > 0) 
            // 模拟售票过程
            try 
                Thread.sleep(100);
             catch (InterruptedException e) 
                e.printStackTrace();
            
            String name = Thread.currentThread().getName();
            System.out.println(name + "正在出售第" + (tickets--) + "张票");
        
    
    @Override
    public void run() 
        while (tickets > 0) 
            sell();
            // 模拟空闲过程
            try 
                Thread.sleep(new Random().nextInt(11) * 100);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
    

以上是关于Java基础教程——线程同步的主要内容,如果未能解决你的问题,请参考以下文章

java基础——线程同步块

Java基础知识笔记(五:多线程的同步问题)

Java基础之线程5-线程同步死锁

Java基础之线程5-线程同步死锁

Java基础之多线程

阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第3节 线程同步机制_4_解决线程安全问题_同步代码块