您的位置:首页 > 职场人生

黑马程序员---java基础多线程

2013-05-25 17:10 453 查看
------- android培训java培训、期待与您交流! ----------

回顾知识:Thread类、模版方法设计模式、匿名内部类实现Runnable开启线程,线程的同步互斥、线程间通信、线程池

计算机中正在执行的程序叫做进程,而线程是进程中某个单一顺序的控制流。多线程是指单个程序中可以同时运行多个不同的线程,执行不同的任务。

线程状态图:



Thread类与模版方法设计模式

首先,启动线程是用start()方法。但线程运行的代码块存在run()方法中。这里Thread类用到了模版方法设计模式。

模版方法设计模式是指:通过把不变的的行为搬移到超类,去除子类中重复的代码来体现它的优势;当不变的和可变的行为在子类实现中混合在一起的时候,不变的行为就会在子类中重复实现,通过模板方法模式把这些行为搬移到单一的地方,这样就可以帮助子类摆脱重复不变行为的纠缠。

线程的同步互斥与线程间的通讯

开启多线程后带来的安全问题:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,

还没有执行完,另一个线程参与进来执行。导致共享数据的错误。解决办法是:对多条操作共享数据的语句,

只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

在java中,用同步代码块就可以解决这个问题。

同步代码块的格式:synchronized(对象){

需要被同步的代码;

}

 同步的好处和弊端。

同步的好处:解决了线程的安全问题。

同步的弊端:相对降低了程序的效率,因为同步外的线程都会判断同步锁。

 同步的前提:

同步中必须有多个线程,并使用同一个锁。

同步函数的锁是this.

 

同步函数和同步代码块的区别:

同步函数的锁是固定的this。

同步代码块的锁是任意的对象。

静态的同步函数使用的锁是:该函数所属的字节码文件对象。

 可以用getClass获取,也可以用 当前类名.class表示。

线程间的通讯:其实就是多个线程在操作同一个资源,但是操作的动作不同。如下代码

体现了java中的等待唤醒机制

class Producer implements Runnable
{
private Resource res;

Producer(Resource res)
{
this.res = res;
}

@Override public void run()
{
// TODO Auto-generated method stub
while(true)
{
res.set("lisi");
}
}

}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
@Override public void run()
{
// TODO Auto-generated method stub
while(true)
{
res.out();
}
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)
{
while(flag)
{
try
{
this.wait();
}
catch (Exception e)
{
// TODO: handle exception
}
}
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
this.notifyAll();
}
public synchronized void out()
{
while(!flag)
{
try
{
this.wait();
}
catch (Exception e)
{
// TODO: handle exception
}
}
System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
flag = false;
this.notifyAll();
}
}
public class ThreadPoolTest
{
/**
* @param args
*/
public static void main(String[] args)
{
Resource res = new Resource();
new Thread(new Producer(res)).start();
}
}

死锁以及简单的死锁示例

死锁产生的原因是同步中嵌套同步,但锁却不同。

class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}

public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+"...if locka ");
synchronized(MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"..if lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"..else lockb");
synchronized(MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+".....else locka");
}
}
}
}
}
}

class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}

class DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}


线程池

线程池是管理线程的高级技术。通常提供如下功能:
    1.通过对线程的管理,更急合理的调配资源。通常,线程池里维护着一组空闲的线程,并向外提供,根据系统繁忙程度动态增加或减少空闲线程的数目。比较高级的还自动检测线程的异常。

    2.通过维护线程池中既存线程,可以节省创建线程的开销,尤其是对于Web Service这里处理频繁,而处理过程又比较快的程序,创建线程的开销是不能忽略的。

java.util.concurrent提供了包括互斥,信号量、诸如在并发访问下执行的很好的队列和散列表之类的集合类以及几个工作队列的实现。该包中的Executors类是一种有效的,广泛使用的以工作队列为基础的线程池的正确实现。
使用Executors中的静态方法newFixedThreadPool(i)方法就可以创建大小为i的线程池,实例属于ExecutorsService类型。再调用execute()方法来启动一个线程,输入的参数即为线程变量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: