Java的多线程学习
这里主要是对我之前学习java多线程知识的一个总结,之前的内容都记在笔记本上。
一.线程的概念
线程是轻量化的进程,而进程就是运行中的程序。一个程序至少包含一个进程,而一个进程至少包含一个线程。线程是进程的执行单元。
进程(Process):是一个动态概念,拥有完整的地址空间,不依赖于线程而独立存在。
线程(Thread):属于进程的一部分,不能单独运行,没有自己的地址空间,与进程内的其他线程一起共享分配给该进程的所有资源。
线程可以有效地提高系统的执行效率,进行多任务的处理,注意是并发而不是并行处理。线程的创建和结束所需要的系统开销要比进程的创建和结束要小得多。
二.Java创建线程
Thread类代表线程,所有线程对象都必须是Thread类或其子类的实例。它在lang包下。
创建线程的三种方法:
1.继承Thread类:重写run()方法
2.实现Runnable接口:实现run()方法(用的最多)
3.用Callable和Future实现:实现call()方法
实际中实现Runnable这种方法用的多,这种情况可以多个线程共享一个目标对象。如下例子,两个新建的线程共享s的实例属性。
例:
class Sample implements Runnable
{
private int i;
public void run()
{
for(;i<100;i++){ System.out.println(Thread.currentThread().getName()+" "+i);}
}
public static void main(String[] agrs)
{
Sample s=new Sample();
new Thread(s,"线程1").start(); //两个线程共享一个Sample对象
new Thread(s,"线程2").start();
}
}
三.线程的状态
新建,就绪,阻塞,运行,死亡
调用Thread的start()方法后,线程进入就绪状态。对于死亡状态,也就是结束状态,run()和call()执行完成,或者抛出一个未捕获的异常,或者直接调用Thread的stop()方法,这三种情况都会让线程进入死亡状态。
Thread.sleep(X)方法让线程进入阻塞状态,直到时间完成,就会进入就绪状态,等待再度被调用。该方法需要处理异常。
Thread.yield()方法让线程进入到就绪状态。
Java线程有10个优先级。
四.线程的同步处理
这段主要是关于synchronized的内容,其他的同步处理在后面的文章中给出。多线程编程伴随着同步处理,多个线程如果同时处理一个数据,这时该怎么办,这就是线程同步要处理的问题。线程的控制Java给出了很多类,特别是Java5后面给出的java.util.concurret下的类。.
同步处理的关键字:synchronized和volatile
synchronized用于修饰代码块和方法,被它修饰过的就称为同步代码块或同步方法。 它是一种“加锁---->修改----->释放锁”。
同步代码块中 synchronized(obj){.........} 锁住的就是obj对象,obj可以是this,代表该对象。运行代码块中内容前,会去对obj进行加锁,如果obj被其他线程锁定了,则它只能等待obj被释放。在加锁期间,其他线程无法获取该资源。Java的io包中很多类都新建了一个object变量当做锁,进行同步处理。
1.synchronized锁住的是对象,不是代码,各种线程之前还是会被调用切换。
2.使用synchronized,必须同时进行同步处理,否有一个线程有synchronized,一个线程没有synchronized,这是没有效果的。没加synchronized的线程根本不会进行加锁。
3.要尽量减少锁的粒度,这样是程序能更大程度的并发。能用代码块的不要用同步方法。
4.synchronized同步机制不仅内存同步,而且具有互斥性。volatile仅仅是内存同步。
5.synchronized的对象应选择不会改变的对象,如final修饰的,或者专门新建的Object,否则会出现意想不到的错误。
volatile关键字用于修饰变量,用于保证所有线程对该变量使用时的一致性。volatile声明变量值的一致性,而static申明类变量的唯一性。volatile使用时可能会是不安全的,如在i++操作下,用volatile进行并发处理可能出错。因为i++(自增运算)在Java下并非是原子操作。
5.死锁
使用同步处理会导致死锁的发生,当两个线程相互等待对方释放同步监视器时就会发生死锁。一旦出现死锁,程序不会有异常,也不会有提示,程序处于阻塞状态。
这里给一个死锁的例子:
public class DeadLock implements Runnable
{
private int a;
private int b;
public DeadLock(int a ,int b)
{
this.a=a;
this.b=b;
}
public void run()
{
synchronized(Integer.valueOf(a))
{
try
{
Thread.sleep(200); //sleep()方法必须进行异常处理,且run()方法不能抛出异常,所以只能用try..catch
synchronized(Integer.valueOf(b))
{
System.out.println("进入内部");
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args)
{
DeadLock d1=new DeadLock(1,2);
DeadLock d2=new DeadLock(2,1);
new Thread(d1).start(); //对1加锁,在对2加锁时,d2那个线程已经对2加了锁,这样两个线程相互等待。
new Thread(d2).start();
System.out.println("主线程结束");
}
}
结果是程序阻塞,无法结束,看不到线程输出"进入内部":
参考:《疯狂Java讲义》
《深入理解Java虚拟机》
http://blog.csdn.net/hsuxu/article/details/9472415