继续以卖票为例
一、线程安全问题的解决
同步的第一种表现形式:同步代码块
思路:
将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程是不允许参与运算的,必须要当期线程把代码执行完毕后,其他线程才可以参与运算
在java中用同步代码块解决这个问题
同步代码块格式:
synchronized(对象)
{
需要被同步的代码部分
}
class Ticket implements Runnable
{
private int num = 100;
Object god = new Object();//也可以自定义,建议使用已有的
public void run()
{
while(true)
{
synchronized(god) //同步
{
if(num>0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"..sale.."+num--);
}
}
}
}
}
public class Main
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread j1 = new Thread(t);
Thread j2 = new Thread(t);
Thread j3 = new Thread(t);
Thread j4 = new Thread(t);
j1.start(); j2.start();
j3.start(); j4.start();
}
}
同步的好处:解决了线程的安全问题
同步的弊端:当一个线程sleep时,释放执行权,执行权会给其他线程,然后进行判断同步锁,判断完又进不去,所以相对以前会降低效率,是在可承受的范围内
二、同步的前提
如果出现,同步后安全问题还存在,就必须了解同步的前提
前提:同步中必须有多个线程并使用同一个锁(一个线程就没必要同步,多个锁就失去同步的价值)
三、同步的第二种表现形式:同步函数
/*
* 需求:
* 两个人到银行存钱,每次存10,每个人都是三次
*
* */
class Bank
{
private int sum;
private Object obj = new Object();
//同步代码代码块表现形式
/*public void add(int num)
{
synchronized(obj)
{
sum += num;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO: handle exception
}
System.out.println("sum = "+sum);
}
}*/
//同步函数表现形式
public synchronized void add(int num)
{
sum += num;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO: handle exception
}
System.out.println("sum = "+sum);
}
}
class Custom implements Runnable
{
private Bank bank = new Bank();
public void run()
{
for(int i = 0;i<3;i++)
{
bank.add(10);
}
}
}
class Main
{
public static void main(String[] args)
{
Custom tCustom = new Custom();
Thread j1 = new Thread(tCustom);
Thread j2 = new Thread(tCustom);
j1.start();
j2.start();
}
}验证同步函数的锁是什么?(了解)
class Ticket implements Runnable
{
private int num = 100;
Object god = new Object();//也可以自定义,建议使用已有的
boolean flag = true;
public void run()
{
if(flag==true)
{
while(true)
{
synchronized(this) //同步
{
if(num>0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"..obj.."+num--);
}
}
}
}
else {
while(true)
this.show();
}
}
public synchronized void show()
{
if(num>0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"..fu.."+num--);
}
}
}
class Main
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread j1 = new Thread(t);
Thread j2 = new Thread(t);
j1.start();
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// TODO: handle exception
}
t.flag = false;
j2.start();
}
}同步函数的使用的锁是this
同步函数和同步代码块的区别:
同步函数的锁是固定的this,同步代码块的锁是任意的
所以同步时,建议使用同步代码块
验证静态同步函数的锁是什么?(了解)
静态同步函数的锁不是this,因为根本没有this
class Ticket implements Runnable
{
private static int num = 100;
Object god = new Object();//也可以自定义,建议使用已有的
boolean flag = true;
public void run()
{
if(flag==true)
{
while(true)
{
synchronized(this.getClass())
//synchronized(Ticket.class)
{
if(num>0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"..obj.."+num--);
}
}
}
}
else {
while(true)
this.show();
}
}
public static synchronized void show()
{
if(num>0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"..fu.."+num--);
}
}
}
class Main
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread j1 = new Thread(t);
Thread j2 = new Thread(t);
j1.start();
try
{
Thread.sleep(10);
} catch (InterruptedException e)
{
// TODO: handle exception
}
t.flag = false;
j2.start();
}
}可以使用getClass方法获取,也可以使用 当前 类名.class获取
原文:http://blog.csdn.net/wjw0130/article/details/39938407