我们在卖票的中间暂停一段时间。观察程序的运行结果。
class Ticket implements Runnable
{
private int ticket = 20;
public void run()
{
while(true)
{
if(ticket>0)
{
try
{
Thread.sleep(100);
}
catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName()+"---sale :"+ticket--);
}
}
}
}
class TicketDemo1
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);//创建了一个线程
Thread t2 = new Thread(t);//创建了一个线程
Thread t3 = new Thread(t);//创建了一个线程
Thread t4 = new Thread(t);//创建了一个线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
通过分析,发现,打印出,0,-1.-2等错票。这是什么原因呢?有人说是博主故意加了sleep,其实吧,正常情况下,cpu要在多个线程之间进行来回切换的,不可以一直运行到底的,当卖票的时候cpu突然就切出去了。所以这里只是模仿了一下。
synchronized(对象)
{
//需要被同步的代码
}
对象如同锁。持有锁的线程可以在同步中进行。没有持有锁的 线程即使获取了cpu的执行权,也进不去,因为没有获得锁。
高铁上的卫生间,一个人进去了,门外就显示红色灯表明已经有人了,等ok出来以后,灯又变绿了。
好处:解决了多线程的安全问题。
弊端:多个线程都需要判断锁,较为消耗资源。
class Ticket implements Runnable
{
private int ticket = 20;
Object o = new Object();
public void run()
{
while(true)
{
synchronized(o) //线程运行到这里判断锁,就相当于一个标志位,获得锁以后就把标志位置位,其他线程进不来。当该线程执行完出去以后,会把标志位置回去。
{
if(ticket>0)
{
try
{
Thread.sleep(100);
}
catch(Exception e)
{
}
System.out.println(Thread.currentThread().getName()+"---sale :"+ticket--);
}
}
}
}
}
class TicketDemo1
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);//创建了一个线程
Thread t2 = new Thread(t);//创建了一个线程
Thread t3 = new Thread(t);//创建了一个线程
Thread t4 = new Thread(t);//创建了一个线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
例子:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次;
class Bank
{
Object obj = new Object();
private int sum;
public void add(int n)
{
synchronized(obj)
{
sum = sum +n;
try
{
Thread.sleep(10);
}
catch(Exception e){}
System.out.println("sum = "+sum);
}
}
}
class Cus implements Runnable
{
private Bank b = new Bank();
public void run()
{
for(int x= 0;x<3;x++)
{
b.add(100);
}
}
}
class BankDemo
{
public static void main(String[] args)
{
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
普通代码块和同步代码块有什么区别呢?其实就是同步代码块有其特有的性质(加锁)。试想函数能否变为同步呢?答案是肯定的!
class Bank
{
Object obj = new Object();
private int sum;
public synchronized void add(int n)
{
sum = sum +n;
try
{
Thread.sleep(10);
}
catch(Exception e){}
System.out.println("sum = "+sum);
}
}
class Cus implements Runnable
{
private Bank b = new Bank();
public void run()
{
for(int x= 0;x<3;x++)
{
b.add(100);
}
}
}
class BankDemo
{
public static void main(String[] args)
{
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
如何找问题?
1、明确哪些代码是多线程运行的代码
2、明确共享数据
3、明确多线程运行代码中哪些语句是操作共享数据的。
Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。假设有两个线程,分别代表两个饥饿的人,他们必须共享刀叉并轮流吃饭。他们都需要获得两个锁:共享刀和共享叉的锁。
假如线程 “A”获得了刀,而线程“B”获得了叉。线程“A”就会进入阻塞状态来等待获得叉,而线程“B”则阻塞来等待“A”所拥有的刀。
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
synchronized(MyLock.locka)
{
System.out.println("if locka!");
synchronized(MyLock.lockb)
{
System.out.println("if lockb!");
}
}
}
else
{
synchronized(MyLock.lockb)
{
System.out.println("if lockb!");
synchronized(MyLock.locka)
{
System.out.println("if locka!");
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}