多线程概述(2)
4.多线程安全问题
(1)安全问题的发生
在售票案例中,极有可能碰到的“意外”情况,比如一张同样的票被打印多次,或者打印的票号为0甚至是负数,这些情况都是由多线程操作共享资源所导致的线程安全问题。
例如
class Ticket implements Runnable
{
private int tickets = 100;//共有100张票
public void run()
{
while(true)
{
if(tickets>0)
{
try{
Thread.sleep(10);//模拟了售票耗时过程。
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在发售第"+tickets--+"张票");//
}
}
}
}
class Demo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
//创建并开启了4个线程,模拟同时售票
new Thread(t,"窗口1").start();
new Thread(t,"窗口2").start();
new Thread(t,"窗口3").start();
new Thread(t,"窗口4").start();
}
}
问题产生的原因:
- 线程任务中在操作共享的数据。
- 线程任务操作共享数据的代码有多条(运算有多个)。
(2)加入同步的好处和缺点
好处:可以解决多线程安全问题
缺点:降低了程序的性能。每个线程都要去判断锁机制,那么会增加程序运行的负担,同时只要做判断,CPU都要处理,那么也会消耗CPU的资源。即就是加同步会降低程序的性能。
举例
class Ticket implements Runnable
{
private int tickets = 100;
//定义同步代码块的标记对象。相当与锁的功能
//private Object obj = new Object();
public void run()
{
while(true)
{
//使用同步代码块解决线程安全问题。
synchronized(new Object())
{
if(tickets>0)
{ //由于run方法是复写接口中的,run方法没有抛出异常, 此时这里只能捕获异常,而不能抛出
try{Thread.sleep(10);}catch(InterruptedException e){
}
System.out.println(Thread.currentThread().getName()+"正在发售第"+tickets--+"张票");
}
}
}
}
}
多个线程操作了共享数据,并且操作共享数据的代码有多句,必须使用同步代码块来解决,当线程任务代码只会有一个线程执行时,加不加同步都可以。当线程任务代码会有被多个线程执行时,这时需要加同步,但是加同步时一定要保证多个线程使用的是同一把锁。上述代码发生的安全问题就是因为每个线程都有自己的Object对象作为自己的锁。
同步的前提:必须保证多个线程在同步中使用的是同一个锁。
(3)多线程问题的应用
class Demo
{
public static void main(String[] args)
{
Bank bank = new Bank();
Consumer cons1 = new Consumer(bank);
Consumer cons2 = new Consumer(bank);
Thread t1 = new Thread(cons1);
Thread t2 = new Thread(cons2);
t1.start();
t2.start();
}
}
class Bank
{
//sum代表银行
private int sum ;
//定义同步的锁对象
private Object obj = new Object();
//给银行存钱
public void add(int num)
{
//加同步解决安全问题
synchronized(obj)
{
sum = sum + num;
//显示银行中总钱数
System.out.println("银行中的钱有:"+sum);
}
}
}
//描述储户,每个用户都会给银行中存放钱,存钱这个行为应该是要被多个线程操作
class Consumer implements Runnable
{
private Bank bank ;
Consumer(Bank bank)
{
this.bank = bank;
}
//多线程要操作的存储钱的过程
public void run()
{
//循环三次,每次存放100元
for (int i=1;i<=3 ;i++ )
{
bank.add(100);
}
}
}