多线程
1、多线程的阐述
几乎所有的操作系统都支持同时运行多个任务,一个任务通常都是一个程序,每个运行中的任务就是一个进程。当一个程序运行的时候,内部可能包含了多个程序执行流,这每一个程序执行流就是一个个单独的线程。线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须拥有一个父进程。线程可以完成一定的任务,他可以和其他线程共享父进程中的资源,相互协作来完成各自的任务。但是线程的运行时独立的,他不知道同一个进程中还有其他的线程的存在,所以他们的运行就是抢占式的,我们在创建线程的时候必须处理好他们的资源共享的问题。多线程的优势:进程间不能共享内存,但是线程间共享内存却是非常容易的。 由于线程的并发性优势,所以使用多线程的效率会高很多
2、线程创建的两种方式
2.1 继承Thread类,复写run()方法。调用start()方法,启动线程。
class ThreadDemo
{
public static void main(String[] args)
{
new MyThread().start();//创建并启动第一个线程
new MyThread().start();//创建并启动第二个线程
}
}
class MyThread extends Thread
{
public void run()
{
//需要线程执行的代码
}
}
注意:启动线程是调用Thread的start()方法,他的底层会自动调用Thread的run()方法。
2.2 实现Runnable接口,复写run()方法,将其传入Thread的构造函数,启动线程
class ThreadDemo2
{
public static void main(String[] args)
{
MyThread my=new MyThread();//创建线程资源对象
new MyThread(my).start();//启动第一个线程
new MyThread(my).start();//启动第二个线程
}
}
class MyThread2 implements Runnable
{
public void run()
{
//需要线程执行的代码
}
}
2.3 两种线程的对比以及其生命周期
我们已经熟悉了线程的两种创建方式,那么这两种线程各自的优缺点就是:实现Runnable接口,还可以去继承其他类,因为java不支持多继承,但是支撑多实现。所以避免了单继承的局限性,在这种情况下,可以让多个线程共享同一个资源,很好的体现了面向对象的思想。
当线程被创建并启动以后,他既不是一启动就进入了执行状态,也不是一直处于执行状态,在线程的生命周期内,他要经过新建、就绪、运行、阻塞和死忙五种状态。尤其是启动之后,进行着线程之间的切换。线程状态的不同决定着当前线程的执行情况。我们可以通过线程的各自方法,让线程处于不同的状态,但是值得注意的就是: 当一个线程死了之后,就不能再用start()方法再来开启他。可以通过isAlive()方法来判断是否死亡。
3、线程的同步---安全问题的解决。
在多个线程并发的访问同一个资源的时候,就会引发安全问题。关于线程安全问题,最显著的例子就是毕老师所举的火车卖票的例子。为了解决这个问题,java的多线程支持引入了同步监听器来解决这个问题,就是同步代码块的出现解决了这个问题。他的格式就是synchronized(Object obj){//需要被同步的代码},这个式子中的obj就是所说的监听器。同步监听器的意思就是:线程开始执行同步代码块前必须先获得对同步监听器的锁定。
同步方法的诞生:与同步代码块相对应的就是java的多线程的安全技术还提供了同步方法,同步方法就是使用关键字synchronized来修饰要监听的方法,对于同步方法来说没有必要来指定监听器对象,因为此时监听的对象就是this,就是对象自身。
从JDK1.5之后,java提供了另外一种线程同步的机制:它通过显示定义同步锁对象来实现同步,在这种机制下,同步锁应该使用Lock对象充当。
通常认为:Lock提供了比synchronized更广泛的锁定操作,并且可以支持多个相关的Condition对象。Lock是控制线程对共享资源进行访问的工具。
使用Lock的一般方法就是先创建Lock对象,在获得condition对象,调用condition的方法进行资源的访问限制。
例如:Lock lock=new ReadWriteLock();
Condition condition=lock.newCondition()
在调用lock.lock();和lock.unLock();这两个方法限制共享资源。
在同步中可以调用condition.await();和condition.signal();这两个方法唤醒持有所的对象。
需要注意的就是:在最后必须释放所资源,也就是说将lock.unLock();必须放在finally语句里面。
在JDK1.5的新特性中出现了ArrayBlockingQuece它里面提及的方法可以实现资源的依次访问。
4、JDK1.5线程的新特性
4.1 Callable和Future的运用
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableandFuture {
public static void main(String[] args) {
Future future=Executors.newSingleThreadExecutor().submit(
new Callable<String>() {
public String call()
{
return "你好";
}
}
);
System.out.println("拿去结果");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
其实,Callable接口和Runnable接口是差不多的,只不过Callable接口的call方法有返回值,Runnable接口的run方法没有返回值,他们都可以当做参数用Executes去启动线程,但是我们可以用Future的get方法等待线程执行完毕后的结果。
4.2 Executors的运用
因为系统启动一个新线程的成本还是比较高的,因为它涉及到与操作系统的相互交互。由此,JDK1.5开启了一个 工厂类来产生线程池---Executors。使用线程池来执行线程任务的步骤如下:
调用Executors类的静态工厂方法创建一个ExecutorsServerce对象,该对象代表着一个线程池。
创建Callable或者Runnable类的实现类作为线程执行任务。
调用ExecutorsServerce对象的submit方法来提交Runnable实例或者Callable实例。
当不像提交任务时候,可以使用shutdown方法来结束线程,但是shutdown方法在执行前会将当前线程的任务执行完,当想立即结束线程的时候,就可以使用shutdownNow()方法。
4.3 包装线程不安全的集合
JDK1.5还提供了ConcurrentHashMap、ConcurrentLinkedQueue这两个类,它支持线程的并发访问集合。当然,集合工具类Collections还提供喜多同步方法可以讲线程不同步的集合编程并发访问安全的。
5、线程之间的通信---等待唤醒机制
为了实现线程之间的相互唤醒机制,我们特具毕老师经典的例子 //实现输入一次,打印一次 交替进行
import java.lang.*;
class ThreadDemo
{
public static void main(String[] args)
{
Res r=new Res();
Inne i=new Inne(r);
Out o=new Out(r);
new Thread(i).start();
new Thread(o).start();
}
}
class Res
{
String name;
int age;
boolean flag=false;
}
class Inne implements Runnable
{ private Res r;
Inne(Res r){this.r=r;}
public void run()
{ int x=0;
while(true)
{
synchronized(r)
{
if(r.flag)
try{r.wait();}catch(Exception e){}
if(x==0)
{r.name="张三";
r.age=30;}
else
{
r.name="lisi";
r.age=90;
}
x=(x+1)%2;
r.flag=true;
r.notify();
}
}
}
}
class Out implements Runnable
{
private Res r;
Out(Res r){this.r=r;}
public void run()
{
while(true)
{
synchronized(r)
{
if(!(r.flag))
try{r.wait();}catch(Exception e){}
else
System.out.println(r.name+r.age);
r.flag=false;
r.notify();
}
}
}
}
以上就是传统的等待唤醒机制。