熟练掌握多线程是成为中高级程序员的必备技能。多线程的功能和意义是很明确的:为了提高吞吐量,提高资源利用率,提高CPU效率。比如你要从一个设备获取数据,然后对数据进行处理。如果只用一个线程从前到后执行,当采集数据IO时cpu是空闲的,当处理数据时又无法IO,这样效率太低,这时就要用到多线程。所以高级程序语言都有专门的针对多线程的套路。
多线程中最复杂的问题就是解决共享冲突问题,假设有两个线程,都对同一个变量进行操作,如何让操作变的有规矩,而不发生冲突。最常见的例子就是银行账户问题,一个账户有1000元,多个ATM机同时对一个账户进行取1000元钱操作,就可能存在一个账户取完了钱,另一个账户仍然可以取钱,因为它判断的时候账户余额还是1000。又或者试想你右手拿着筷子想夹一块肉,这块肉却突然被另一个人吃掉了。
JAVA里提供synchronized关键字解决多线程共享问题。
如:
-
class Account {
-
-
String name;
-
float amount;
-
-
public Account(String name,float amount){
-
this.name=name;
-
this.amount=amount;
-
}
-
public synchronized void deposit(float amt){
-
float tmp = amount;
-
tmp+=amt;
-
try {
-
Thread.sleep(1);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
amount = tmp;
-
}
-
public synchronized void withdraw(float amt){
-
float tmp = amount;
-
tmp-=amt;
-
try {
-
Thread.sleep(1);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
amount=tmp;
-
}
-
public float getBalance(){
-
return amount;
-
}
-
}
-
-
public class Test{
-
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);
-
long time1 = System.currentTimeMillis();
-
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
-
}
-
}
-
long time2 = System.currentTimeMillis();
-
System.out.println("Finally, John's balance is:" + acc.getBalance()+"go on time:"+(time2-time1)+"ms");
-
}
-
}
输出结果为:Finally, John's balance is:1000.0go on time:2117ms
-
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(1);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
amount = tmp;
-
}
-
public void withdraw(float amt){
-
float tmp = amount;
-
tmp-=amt;
-
try {
-
Thread.sleep(1);
-
} catch (InterruptedException e) {
-
// TODO Auto-generated catch block
-
e.printStackTrace();
-
}
-
amount=tmp;
-
}
-
public float getBalance(){
-
return amount;
-
}
-
}
-
-
public class Test{
-
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);
-
long time1 = System.currentTimeMillis();
-
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
-
}
-
}
-
long time2 = System.currentTimeMillis();
-
System.out.println("Finally, John's balance is:" + acc.getBalance()+"go on time:"+(time2-time1)+"ms");
-
}
-
}
去掉synchronized输出结果为:Finally, John's balance is:8800.0go on time:318ms(这个输出结果是随机的)
从上面可以看出,使用synchronized可以使线程按序进行,它是这么实现的:当一个线程使用synchronized方法时首先会获得该对象的锁,然后执行完代码再释放锁,其他线程在它释放锁之前无法获得该锁就无法执行synchronized方法,这样就保证了每时每刻只有一个线程才可以执行synchronized方法。同时从执行时间来看,使用synchronized后会拖延线程的执行速度的。这就要求我们不要把过多的操作放在一个synchronized方法中,可以采用synchronized块解决问题。
-
class Foo extends Thread
-
{
-
private String name;
-
private String val;
-
public Foo(String name,String v)
-
{
-
this.name=name;
-
val = v;
-
}
-
public void printVal()
-
{
-
synchronized(val) {
-
while(true) System.out.println(name+val);
-
}
-
}
-
public void run()
-
{
-
printVal();
-
}
-
}
-
public class Test
-
{
-
public static void main(String args[])
-
{
-
Foo f1 = new Foo("Foo 1:","printVal");
-
f1.start();
-
Foo f2 = new Foo("Foo 2:","printVal");
-
f2.start();
-
}
-
}
输出结果:Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal
Foo 1:printVal(无限)
用synchronized锁住了string对象val,线程1占据对象的锁,一直不放开,线程2就无法执行该代码块。从这里可以看出synchronized锁住的是一个类的对象,而使用这个对象的同步方法时就需要先获得锁。
阅读(1224) | 评论(0) | 转发(0) |