Java多线程设计模式(1)

来源:互联网 发布:热血仙境源码一键端 编辑:程序博客网 时间:2024/04/29 12:52
 Java多线程设计模式(1)
2013-05-16 14:10:13
标签:多线程 设计模式 synchronized
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://computerdragon.blog.51cto.com/6235984/1201769

1 线程中的几个方法解释

Thread.sleep(long 毫秒)

在指定的时间内让当前线程休眠,将控制权交给其他线程,休眠的线程依旧拥有它所占用的锁

Thread.yield();

暂停或者放弃当前正在执行的线程,并且执行其他线程,但是并没有释放该线程所拥有的锁,线程放弃后,让其他相同或者更高线程得以运行。

t.join()

A线程中等待t线程,可以指定一定时间后继续执行或者无限等待

t.interrupt()

中断t的线程,可以唤醒打断处于sleepjoinwait状态下的线程,

p.wait()

线程可以等待一个它锁定的对象,在等待的时候,它会释放此对象的锁并且暂停进入休眠,它会在时间到期、该线程被中断和对象得到通知之前一直保持休息。直到它得到了其他线程的通知,如该对象调用notify()notifyAll()的时候才会执行

对于使用wait和notify与notifyAll的时候均要先利用同步代码块或者同步方法获取该对象的锁,利用wait后,会首先获取该对象锁,然后进入该对象的wait set中,暂停当前的线程,释放该对象的锁,在该对象的wait set等待着

对于直接wait()就是this.wait(),执行wait()的线程会在this的wait set中等待着。

Yield和Sleep区别

       sleep 方法使当前运行中的线程睡眠一段时间,进入不可以运行状态,这段时间的长短是由程序设定的,它只有被吵醒或者睡眠时间到才会继续运行;

       yield方法使当前线程让出CPU占有权即时间片,重新排队,但让出的时间是不可设定的,该线程依然是可运行状态

       yield()方法对应了如下操作:先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给该线程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。

sleep 方法允许较低优先级的线程获得运行机会yield()方法执行时,当前线程仍处在运行状态,它在队列中等候cpu调用,所以说是可运行状态(注意不是运行状态),所以不可能让出较低优先级的线程此时获取CPU占有权。在一个运行系统中,如果较高优先级的线程没有调用sleep方法,也没有受到I/O阻塞,那么较低优先级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。

yield()只是使当前线程重新回到可运行状态,所有执行yield()的线程有可能在进入到可运行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会
2 线程的启动

   线程的启动永远都是调用Start()方法开始的, Thread.start ()方法启动线程,使之进入就绪状态,当cpu 分配时间片给该线程时,由 JVM 调度执行 run ()方法之所以要startrun方法的原因是,因为 JVM创建一个单独的线程不同于普通方法的调用,启动一个线程是由start 方法完成,start 由本地方法实现,需要显示地被调用。Run方法是真正执行的任务部分,另一个好处就是在于当一个对象需要继承多类的时候,此时就可以将具体的任务实现了 Runnable 接口,然后传递给Thread中,避免因继承了Thread 类而造成的 Java 的多继承问题。

   对于同一个Thread类的实例变量,只能调用一次Start方法,一旦调用start方法,就会变成结束start状态,当其再次调用start方法的时候,就会出现IllegalThreadStateException,就会进行退让让线程的启动不会再次执行。Thread类的start方法采用的就是Balking Pattern模式。

   多线程都是针对多个线程操作同一个对象下作用的。


3 Single Threaded Execution Pattern

   Single Threaded Execution指的就是“只允许一个线程执行”的意思,它表明对于调用这个方法或对象的时候必须只能让一个线程来执行。利用synchronized来设置对象的监听器,在synchronized方法体或者同步块中。必须要注意synchronized实例方法共享的是类的实例对象的锁,而synchronized类方法即static synchronized的方法共享的是类的锁,这两把锁完全不同。

   对于一个对象的某一个方法,当有多个线程需要访问的时候,这些线程是共享这个对象的所有成员属性的。线程是共享这个对象的内存。

   Single Threaded Execution中,作为共享资源的类,即可以被多个线程访问的。有两种方法,安全方法和非安全方法,所谓的安全方法就是多个线程调用也不会出现问题。而非安全方法在多个线程访问的过程可能会出现问题。需要对于非安全方法做处理。
适用性:当共享资源类的实例可能被多个线程访问的时候,实例的状态即属性会被修改,在多个线程中,此时就需要对于对于实例的状态发生变化的范围作为临界区,利用synchronized来保护,实现临界区间,来监听多线程的访问。


4 Immutable Pattern

   Immutable Pattern指的就是“有着能够保证实例的状态绝不会发生变化更改的类”,再次说明这里指实例的状态都是指的是类的成员属性。所谓的不能发生变化,也就是对于类的状态只允许赋值一次,且不会给外界提供任何setter方法,并且属性是private final,一般通过在构造器来对于这些状态赋值,当然也可以将类声明为final。利用这种方式,就克服利用共享互斥机制来造成的时间浪费。

   Immutable Pattern参与者是一个字段值无法更改的类,也没有任何用来更改字段值的方法。当Immutable参与者的实例一旦创建后,状态就固定下来,无法更改了。
适用性:当实例产生后,状态不再发生变化。实例需要多个线程共享,访问很频繁的时候。


5 Guarded Suspension Pattern--要等到我准备好再通知你们哟

  Guarded Suspension指的意思是“在多线程中,当线程访问某一个资源时候,此时并不适合马上执行某个操作时,就要求想要执行该操作的线程必须等待。”Guarded是“被保护的”,suspension是“暂停”的意思。

  Guarded Suspension要求线程必须等待。利用的就是wait,再通过notifyAll来通知所有等待的线程。将线程要执行某一个操作,必须满足的条件称为警戒条件。每个线程在执行的时候,如果不满足警戒条件就必须在等待,只有收到通知后,判断警戒条件成功后,才能进而执行所要的目的操作。

  常用的警戒条件结构如下:

 public synchronized void methodName()

 {

      while(警戒条件的否定)

          使用wait;//这里直接在共享类中wait,所有等待的都会进入该类实例的this wait set中等候

     //跳出循环,说明满足警戒条件

      执行目的操作。

  }
注:不能将while替换为if,因为凡是处于等待中的线程一旦获得唤醒,就必须在执行目的操作之前先进行警戒条件的判断才能进而执行目的操作。必须将捕获wait的异常包括在while循环体中,否则一旦发生异常可能就会进而执行目的操作。这里的wait也不能换为sleep,因为利用sleep该线程会一直拥有它所获得锁。

在这里wait和notify都是隐藏在共享资源中,这样凡是使用该共享资源的线程就不需要考虑这些问题了。

GuardedObject参与者是一个拥有被防卫的方法guardedMethod,一般就是利用synchronized来修饰,在线程执行这个方法的时候,只要满足警戒条件就会执行,否则就会一直等待。警戒条件会随着实例的状态变化而变化。还需要一个更改实例状态的方法stateChangingMethod,来通知那些等待的线程。

一般使用while语句和wait方法来实现guardedMethod,使用notify和notifyAll来实现stateChangingMethod


6 Balking Pattern

 Balking Pattern指的是“多线程访问共享资源时候,当现在不适合进行这个操作,或者没有必要进行这个操作的时候,此时就会直接放弃进行这个操作而返回”。

 Balking Pattern也是需要警戒条件的。GuardedObject参与者是一个拥有被防卫的方法guardedMethod,一般就是利用synchronized来修饰,在线程执行这个方法的时候,只要满足警戒条件就会执行,否则就会。警戒条马上返回,直接退出该方法。警戒条件的成立会随着实例的状态变化而变化。还需要一个更改实例状态的方法stateChangingMethod,来通知那些等待的线程。

  警戒条件的结构:

 public synchronized void guardedMethod()

 {

     if(警戒条件否定)

        return ;

       //警戒条件满足情况

     目的操作

   }
适用性:在线程访问警戒条件不成立的时候,不需要刻意执行等待的时候,不想一直等待警戒条件成立的时候,或者警戒条件只有第一次成立的时候。注意线程的start方法就是利用这种方式的,即只有第一次成立的时候才会调用,第二次调用就会出错。


 Java多线程设计模式(2)生产者与消费者模式
2013-05-17 14:49:08
标签:java 生产者与消费者模式 多线程
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://computerdragon.blog.51cto.com/6235984/1202679

1 Producer-Consumer Pattern

Producer-Consumer Pattern主要就是在生产者与消费者之间建立一个“桥梁参与者”,用来解决生产者线程与消费者线程之间速度的不匹配。

   当要从某个线程Produccer参与者将数据传输给其它线程Consumer参与者的时候,此时就可以在中间加一个Channel参与者,在Channel参与者中以某种方式存放接受的数据,再以某方式来获取收到的数据,Channel就可以来缓存两个线程之间传输的数据,在Channel参与者为了保证安全性,也要用Guarded Suspension Pattern模式。

   Channel参与者作为一个中间者,当Channel参与者从Producer参与者接收到数据,可以用三种方式将数据按顺序 传递给Consumer参与者。

1 队列,这是一种按照FIFO的方式存储数据,即最先到达的数据最先传输给Consumer参与者。在Java中,可以利用数组形式来存放,每次从数组下标最前端获取数据,而从数组下标最后端来缓存数据。也可以利用LinkedList来存放,每次缓存数据的时候,利用LinkedList.addLast(obj),每次获取数据的时候利用LinkedList.removeFirst();移除并且返回队列的第一个元素。

2 堆栈,这是一种以LIFO的方式存储数据,即最先到达的数据最后传输给Consumer参与者。在Java中,对于堆栈的实现,可以直接使用LinkedList,利用pop()从栈顶弹出一个数据来获取数据,利用push(obj)来向堆栈中缓存一个数据

  3 优先级队列,对于缓存的数据设置一些优先级来存储。

  生产者与消费者模式,其实就是线程之间的合作关系,同时又包含了互斥关系。所谓的合作就是生产者生成产品,提供消费者消费。所谓的互斥就是生产者和消费者对于中间的缓冲区是互斥访问的。

实例:

   几个厨师制作食物,将物品放置在桌子上,但是桌子放置的盘子有限,消费者可以从桌子上获取食物来吃。当桌子上有空位置的时候,厨师就可以继续放置做好的食物,且通知消费者来吃,但是满了就只能一直等待消费者吃了有空的位置。而消费者每次取食物的时候,如果桌子上面有食物,则就取走,并且通知厨师来做食物,如果没有则就等待。

生产者Producer代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package whut.producer;
import java.util.Random;
public class MakerThread extends Thread{
    private final Table table;
    private final Random random;
    private static int id=0;
    public MakerThread(String name,Table table,long seed)
    {
        super(name);
        this.table=table;
        this.random=new Random(seed);
    }
                                                                        
    public void run()
    {
        try{
            while(true)
            {
                Thread.sleep(random.nextInt(1000));
                String cake=" [Cake No."+nextId()+" by "+Thread.currentThread().getName()+"]";
                table.put(cake);
                                                                                    
            }
        }catch(InterruptedException e)
        {
        }
    }
    //为了使得所有实例共享该字段
    public static synchronized int nextId()
    {
        return id++;
    }
}

消费者Consumer代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package whut.producer;
import java.util.Random;
public class EaterThread extends Thread{
    private final Table table;
    private final Random random;
    public EaterThread(String name,Table table,long seed)
    {
        super(name);
        this.table=table;
        this.random=new Random(seed);
    }
    public void run()
    {
        try{
            while(true)
            {
                String cake=table.take();
                Thread.sleep(random.nextInt(1000));
            }
        }catch(InterruptedException e)
        {
        }
    }
}

Channel中间缓冲区,关键部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package whut.producer;
public class Table {
private final String[] cakes;//利用数组来作为缓冲区
    private int head;//下一次蛋糕取的位置
    private int tail;//下一次蛋糕放置位置
    private int count;//桌子上蛋糕的总数
    public Table(int count)
    {
        this.cakes=new String[count];
        this.head=0;
        this.tail=0;
        this.count=0;
    }
                           
    public synchronized void put(String cake)throws InterruptedException
    {
        System.out.println(Thread.currentThread().getName()+" puts "+cake);
        while(count>=cakes.length)
        {
            System.out.println(Thread.currentThread().getName()+" Begin wait....");
            wait();
            System.out.println(Thread.currentThread().getName()+" End wait....");
        }
        cakes[tail]=cake;
        tail=(tail+1)%cakes.length;
        count++;
        notifyAll();
    }
                           
    //取蛋糕
    public synchronized String take()throws InterruptedException
    {
        while(count<=0)
        {
            System.out.println(Thread.currentThread().getName()+" Begin wait....");
            wait();
            System.out.println(Thread.currentThread().getName()+" End wait....");
        }
        String cake=cakes[head];
        head=(head+1)%cakes.length;
        count--;
        notifyAll();
        System.out.println(Thread.currentThread().getName()+" gets "+cake);
        return cake;
    }
}

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package whut.producer;
public class MainTest {
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Table table=new Table(3);
        new MakerThread("MakerThread-1",table,31415).start();
        new MakerThread("MakerThread-2",table,92653).start();
        new MakerThread("MakerThread-3",table,58979).start();
                         
        new EaterThread("EaterThread-1",table,32384).start();
        new EaterThread("EaterThread-2",table,32384).start();
        new EaterThread("EaterThread-3",table,32384).start();
        //可以通过调用interrupt来去中断结束任何线程
    }
}

原创粉丝点击