Java-多线程-线程同步

线程同步

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

线程不安全例子

//卖票例子
public class Tickets implements Runnable{
    private int tickets = 10;
    @Override
    public void run() {
        while (tickets > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tickets--;
            System.out.println(Thread.currentThread().getName() + "线程正在卖票,余票:" + tickets);
        }
    }
}
public class TicketsTest {
    public static void main(String[] args) {
        Tickets tickets = new Tickets();
        new Thread(tickets).start();
        new Thread(tickets).start();
        new Thread(tickets).start();
        //三个线程同时卖票
    }
}
运行结果
Thread-0线程正在卖票,余票:7
Thread-1线程正在卖票,余票:8
Thread-2线程正在卖票,余票:9
Thread-0线程正在卖票,余票:5
Thread-1线程正在卖票,余票:6
Thread-2线程正在卖票,余票:4
Thread-2线程正在卖票,余票:2
Thread-0线程正在卖票,余票:1
Thread-1线程正在卖票,余票:3
Thread-1线程正在卖票,余票:0
Thread-0线程正在卖票,余票:-2
Thread-2线程正在卖票,余票:-1

出现的问题

  • 票数余数不是依次递减的
  • 出现票数余数为复数的情况

线程同步解决线程安全问题

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

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

完成同步操作的三种方式

  • 同步代码块
  • 同步方法
  • 锁机制

同步代码快

  • 同步代码块:synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实现互斥访问。
//格式
synchronized(同步锁) {
 ...
}
  • 同步锁:对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁。
    • 锁对象可以是任意类型
    • 多个线程对象要使用同一把锁

Tips:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能等待锁被释放,再次和其他线程抢夺锁对象

public class TicketsSynchronized implements Runnable{
    private int tickets = 10;
    Object object = new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    tickets--;
                    System.out.println(Thread.currentThread().getName() + "线程正在卖票,余票:" + tickets);
                }
            }
        }
    }
}
public class TicketsSynchronizedTest {
    public static void main(String[] args) {
        TicketsSynchronized tickets = new TicketsSynchronized();
        Thread t1 = new Thread(tickets);
        Thread t2 = new Thread(tickets);
        Thread t3 = new Thread(tickets);
        t1.start();
        t2.start();
        t3.start();
    }
}
//运行结果
Thread-0线程正在卖票,余票:9
Thread-0线程正在卖票,余票:8
Thread-1线程正在卖票,余票:7
Thread-2线程正在卖票,余票:6
Thread-1线程正在卖票,余票:5
Thread-0线程正在卖票,余票:4
Thread-1线程正在卖票,余票:3
Thread-2线程正在卖票,余票:2
Thread-1线程正在卖票,余票:1
Thread-1线程正在卖票,余票:0

同步方法

同步方法:使用synchronized修饰的方法就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。

public synchronized void method(){
    ...
}

谁是同步锁?对于非static方法,同步锁就是this对于static方法,我们使用当前方法所在类的字节码对象(类名.class)作为同步锁

public class TicketsSynchronizedMethod implements Runnable{ 
    private static int tickets = 10;
	  @Override
    public void run() {
        while (true) {
            sellTickets();
        }
    }
    public synchronized void sellTickets(){
        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tickets--;
            System.out.println(Thread.currentThread().getName() + "线程正在卖票,余票:" + tickets);
        }
    }
}

同步方法中的锁对象
同步方法所在类的对象,也就是this

public void sellTickets(){
  synchronized(this){
   if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tickets--;
            System.out.println(Thread.currentThread().getName() + "线程正在卖票,余票:" + tickets);
        }
  }
 }

静态同步方法的锁对象
不可以是this,因为静态属性优先于对象存在
静态同步方法的锁对象是所在类的class属性既class文件对象

public static void sellTickets(){
  synchronized(TicketSynchronizedMethod.class){
   if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tickets--;
            System.out.println(Thread.currentThread().getName() + "线程正在卖票,余票:" + tickets);
        }
  }
 }
public class TicketsSynchronizedMethodTest {
    public static void main(String[] args) {
        TicketsSynchronizedMethod tickets = new TicketsSynchronizedMethod();
        new Thread(tickets).start();
        new Thread(tickets).start();
        new Thread(tickets).start();
    }
}
//运行结果
Thread-0线程正在卖票,余票:9
Thread-0线程正在卖票,余票:8
Thread-2线程正在卖票,余票:7
Thread-1线程正在卖票,余票:6
Thread-2线程正在卖票,余票:5
Thread-0线程正在卖票,余票:4
Thread-2线程正在卖票,余票:3
Thread-1线程正在卖票,余票:2
Thread-2线程正在卖票,余票:1
Thread-0线程正在卖票,余票:0

锁机制

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

Lock锁也称同步锁,加锁与释放锁方法化。

  • public void lock():加同步锁
  • public void unlock():释放同步锁
public class TicketsLock implements Runnable{
    private int tickets = 10;
    Lock lock = new ReentrantLock();
    //创建锁对象
    @Override
    public void run() {
        while (true) {
            lock.lock();
            //加锁
            if (tickets > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                tickets--;
                System.out.println(Thread.currentThread().getName() + "线程正在卖票,余票:" + tickets);
            }
            lock.unlock();
        }
    }
public class TicketsLockTest {
    public static void main(String[] args) {
        TicketsLock tickets = new TicketsLock();
        new Thread(tickets).start();
        new Thread(tickets).start();
        new Thread(tickets).start();
    }
}
//运行结果
Thread-1线程正在卖票,余票:9
Thread-1线程正在卖票,余票:8
Thread-0线程正在卖票,余票:7
Thread-2线程正在卖票,余票:6
Thread-1线程正在卖票,余票:5
Thread-0线程正在卖票,余票:4
Thread-2线程正在卖票,余票:3
Thread-1线程正在卖票,余票:2
Thread-0线程正在卖票,余票:1
Thread-2线程正在卖票,余票:0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值