JAVA多线程事例参考(一)

一. Java多线程安全问题,模拟银行存款

/**
 * 
 *银行账户对象,存取款操作
 *
 */
public class Account {
	String name;
    float amount;        
    public Account(String name, float amount) {
        this.name = name;
        this.amount = amount;
    }
    // 存款
    public  void deposit(float amt) {
        float tmp = amount;
        tmp += amt;        
        try {
            Thread.sleep(100);//模拟其它处理所需要的时间,比如刷新数据库等
        } catch (InterruptedException e) {
            // ignore
        }        
        amount = tmp;
    }
    // 取款
    public  void withdraw(float amt) {
        float tmp = amount;
        tmp -= amt;
        try {
            Thread.sleep(100);//模拟其它处理所需要的时间,比如刷新数据库等
        } catch (InterruptedException e) {
            // ignore
        } 
        amount = tmp;
    }
    public float getBalance() {
        return amount;
    }
}

 

/**
 * 
 * 多线程模拟银行存取款,如果没有采取同步,就会出现多线程安全问题,
 * 则最终的银行账户会出现金额不一致的情况。
 *
 */
public class AccountTest {

	private static int NUM_OF_THREAD = 1000;
    static Thread[] threads = new Thread[NUM_OF_THREAD];
    
    public static void main(String[] args){
        final Account acc = new Account("John", 1000.0f);
        for (int i = 0; i< NUM_OF_THREAD; i++) {
            threads[i] = new Thread(new Runnable() {
                public void run() {
                        acc.deposit(100.0f);
                        acc.withdraw(100.0f);
                }
            });
            threads[i].start();
        }

        for (int i=0; i<NUM_OF_THREAD; i++){
            try {
                threads[i].join(); //等待所有线程运行结束
            } catch (InterruptedException e) {
                // ignore
            }
        }
        System.out.println("Finally, John's balance is:" + acc.getBalance());
    }
}

 

二、Java多线程之synchronized

/**
 * 
 * synchronized中同步的对象必须是多线程中访问的同一个对象,否则达不到同步效果,
 * 也可用synchronized(this)代表当前对象。
 * 如果为synchronized(name)则达不到同步效果。
 *
 */
public class Foo extends Thread {
	private int val;	
	// 通过对公共对象静态变量加锁
	private static Object lock = new Object();
	public Foo(int v) {
		val = v;
	}	
	// 交叉输出(1,3)
	public synchronized void printVal1(int v) {
			while(true){
				System.out.println(v);
			}
	}	
	// 类同步,实现printVal是断面,输出只能是1或者只能是3而不能是两者同时出现
	public void printVal2(int v) 
	{ 
		synchronized(Foo.class) {
			while(true){
				System.out.println(v);
			}
		} 
	} 	
	// 同步的一般原理是应该尽量减小同步的粒度以到达更好的性能,比类同步(printVal2)效果更忧
	public void printVal3(int v) 
	{ 
		synchronized(lock) {
			while(true){
				System.out.println(v);
			}
		} 
	}	
	public void run() {
//		printVal1(val);
//		printVal2(val);
		printVal3(val);
	}
}

 

public class FooTest {	
	public static void main(String args[]){
		Foo f1 = new Foo(1); 
		f1.start(); 
		Foo f2 = new Foo(3);
		f2.start(); 
	}
}

 

三. Java多线程之ThreadLocal

   事例一:

/**
 * 
 *在Java的多线程编程中,为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻
 *只有一个线程对共享变量进行操作。但在有些情况下,synchronized不能保证多线程对共享变量的正确读写。
 *对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”
 *的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,
 *因此可以同时访问而互不影响。
 */
public class QuerySvc {

	private String sql;
	private static ThreadLocal<String> sqlHolder = new ThreadLocal<String>();

	public QuerySvc() {
	}	
	
	/**
	 * 从运行结果可以看出sql变量中值不能保证在execute中值和set设置的值一样, 在web应用中就表现
	 * 为一个用户查询的结果不是自己的查询条件返回的结果,而是另一个用户查询条件的结果;	 
	 * 而ThreadLocal中的值总是和set中设置的值一样,这样通过使用ThreadLocal获得了线程安全性。 
	 */	
	public void execute() {
		System.out.println("Thread " + Thread.currentThread().getId()
				+ " Sql is " + sql);
		System.out.println("Thread " + Thread.currentThread().getId()
				+ " Thread Local variable Sql is " + sqlHolder.get());
	}
	public String getSql() {
		return sql;
	}
	public void setSql(String sql) {
		this.sql = sql;
		sqlHolder.set(sql);
	}
	
	private static class Work extends Thread{		
		private QuerySvc querySvc;		
		private String sql;		
		public Work(QuerySvc querySvc,String sql){
			this.querySvc = querySvc;
			this.sql = sql;
		}		
		 public void run() {
			 querySvc.setSql(sql);
			 querySvc.execute();
		 }
	}	
	public static void main(String args[]){		
		QuerySvc  querySrc = new QuerySvc();		
		for(int k=0;k<10;k++){
			String sql = "Select * from table where id =" + k;
			new Work(querySrc,sql).start();
		}		
	}		
}

 

   事例二:

 

/**
 * 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
 * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
 * 如果要保存多个线程变量,可定义多个ThreadLocal变量,
 * 或者将多个变量封装到一个对象中,将整个对象保存到ThreadLocal变量。
 *
 */
public class SequenceNumber {	
	private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
		public Integer initialValue(){
			return 0;
		}
	};	
	public Integer getNextNum(){
		seqNum.set(seqNum.get()+1);
		return seqNum.get();
	}
	
	// 测试方法
	public static void main(String args[]){
		SequenceNumber sn = new SequenceNumber();
		// 3个线程共享sn,各自产生序列号 
		TestClient t1 = new TestClient(sn); 
		TestClient t2 = new TestClient(sn); 
		TestClient t3 = new TestClient(sn); 
		t1.start(); 
		t2.start(); 
		t3.start();
	}
	
	// 内部静态线程类
	private static class TestClient extends Thread{
		private SequenceNumber sn; 
		public TestClient(SequenceNumber sn) { 
			this. sn = sn; 
		} 
		public void run() 
		{ 
			//每个线程打出3个序列值 
			for (int i = 0; i < 3; i++) {
				System.out.println("thread["+Thread.currentThread().getName()+ 
				"] sn["+sn.getNextNum()+"]"); 
			}
		}
	}
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值