layout | title | date | updated | tags | categories | permalink | thumbnail | toc | comment | notag | top | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
3-3-案例-银行存钱&同步函数 |
2015-10-09 |
2015-10-09 |
|
Java多线程基础 |
true |
true |
false |
false |
- 两个客户到一个银行去存钱,每个客户一次存100,存三次。
- 问题:改程序是否有安全问题,如果有写出分析过程并定义解决方案
class Bank{
private int sum;
private Object obj = new Object();
public void add(int num)
{
sale(num);
}
/**
* 需要同步的同步函数
* @param num
*/
private /*synchronized*/ void sale(int num)
{
sum = sum+num;
/* 此处可能发生CPU切换 */
System.out.println("sum " + sum);
}
}
class Consumer implements Runnable{
private Bank bank = new Bank();
public void run() {
for (int i = 0 ; i < 3 ; i ++)
{
bank.add(100);
}
}
}
public class ThreadDemo{
public static void main(String[] args) {
Consumer consumer = new Consumer();
Thread t1 = new Thread(consumer);
Thread t2 = new Thread(consumer);
t1.start();
t2.start();
}
}
sum 200
sum 200
sum 300
sum 400
sum 500
sum 600
- 既然是多线程问题,必然发生在线程任务内
- 任务代码中是否有共享数据呢? 操作此数据的代码时候有多条任务呢?
private Bank bank = new Bank();
public void run() {
for (int i = 0 ; i < 3 ; i ++)
{
bank.add(100);
}
}
private int sum;
private /*synchronized*/ void sale(int num)
{
sum = sum+num;
/* 此处可能发生CPU切换 */
System.out.println("sum " + sum);
}
bank是共享数据,具体的讲,bank中的sum是共享数据,并且有多条代码操作此数据.
所以我们需要给操作共享变量的函数加同步
class Bank{
private int sum;
private Object obj = new Object();
public void add(int num)
{
sale(num);
}
/**
* 同步函数
* @param num
*/
private synchronized void sale(int num)
{
sum = sum+num;
/* 此处不再发生CPU切换 因为其他线程得不到锁*/
System.out.println("sum " + sum);
}
}
class Consumer implements Runnable{
private Bank bank = new Bank();
public void run() {
for (int i = 0 ; i < 3 ; i ++)
{
bank.add(100);
}
}
}
public class ThreadDemo{
public static void main(String[] args) {
Consumer consumer = new Consumer();
Thread t1 = new Thread(consumer);
Thread t2 = new Thread(consumer);
t1.start();
t2.start();
}
}
- 同步的另外一个体现形式:同步函数 private synchronized void sale(int num)
- 同步函数使用this做锁 , 因为函数必须被对象调用 (可写一个同步代码块,写一个同步函数,同步代码块以this为锁,如果同步代码块中锁对象是同一个 ,那么这一块代码不可被同步执行.)
class Ticket implements Runnable
{
private int tickets = 100;
private Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag){
while(true){
synchronized(this){
if(tickets>0){
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...obj..."+tickets--);//打印线程名称。
}
}
}
}
else{
while(true){
this.sale();
}
}
}
public synchronized void sale()//同步函数,使用的锁对象 this。
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...sale..."+tickets--);//打印线程名称。
}
}
}
class ThreadDemo4
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(InterruptedException e){}
//切换标记,之前,让主线程停一会,这时就只有一个t1线程在,它就会执行同步代码块。
t.flag = false;
t2.start();
}
}
- 同步函数使用的锁是固定的this。当线程任务中只需要一个同步时完全可以使用同步函数
- 同步代码块使用的锁是任意的对象。当线程任务中需要多个同步时,必须通过锁来区分
- 静态同步函数使用的锁不是this而是字节码文件对象 类名.class (一般当只有一个锁时只用同步函数就可以)
静态同步函数使用.class对象做锁
/*
static 同步函数,使用的锁不是this,而是字节码文件对象, 类名.class
*/
class Ticket implements Runnable
{
private static int tickets = 100;
private Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag){
while(true){
synchronized(Ticket.class){
if(tickets>0){
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...obj..."+tickets--);//打印线程名称。
}
}
}
}
else{
while(true){
this.sale();
}
}
}
public static synchronized void sale()//
{
if(tickets>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+"...sale..."+tickets--);//打印线程名称。
}
}
}
class ThreadDemo5
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(InterruptedException e){}
//切换标记,之前,让主线程停一会,这时就只有一个t1线程在,它就会执行同步代码块。
t.flag = false;
t2.start();
}
}