图解Java多线程设计模式 序章1 Java线程

【Thread类的run方法和start方法】

    JAVA程序运行时,最开始运行的只能是主线程。所以必须在程序中启动新线程。

    启动线程时,要使用如下类(一般称为Thread类)

public class MyThread extends Thread {

     public void run(){

          for(int i=0;i<100;i++){

              System.out.println("Nice!");

          }

     }

}

  新启动的线程的操作都编写在run方法中。新线程启动后会调用run方法。随后,当run方法执行结束时,线程也会跟着终止。

  用户启动线程的代码如下

public static void main(String[] args){

    MyThread t = new MyThread();      //主线程创建MyThread类的实例

    t.start();                        //由主线程启动新线程

    for(int i=0;i<100;i++){

         System.out.println("Good!");

    }

}

start方法是Thread类中的方法,用于启动新的线程。

调用start方法后,程序会在后台启动新的线程。然后,由这个新线程调用run方法。

【顺序、并行与并发】

顺序用于表示多个操作 “依次处理”。比如把十个操作交给一个人处理时,这个人要一个一个地按顺序来处理。

并行用于表示多个操作“同时处理”。比如十个操作分给两个人处理时,这两个人就会并行来处理。

扫描二维码关注公众号,回复: 3010302 查看本文章

并发相对于顺序和并行来说比较抽象,用于表示“将一个操作分割成多个部分并且允许无序处理”。比如将十个操作分成相对独立的两类,这样便能够哦开始并发处理了。

【线程的启动】

启动线程的方法有如下两种:

    1    利用Thread类的子类的实例启动线程

    2    利用Runnable接口的实现类的实例启动线程

线程的启动一:利用Thread类的子类

    new MyThread().start();

线程的启动二:利用Runnable接口

    Runnable接口包含在java.lang包中,声明如下

public interface Runnable {

    public abstract void run();

}

Runnable接口的实现类必须要实现run方法。

创建Runnable接口的实现类。将实现类的实例作为参数传给Thread的构造函数,调用start方法。

  new Thread(new Printer("Good")).start();

(小知识java.util.concurrent.ThreadFactory中的线程创建)

ThreadFactory就是一个线程工厂也就是负责生产线程的。java.util.concurrent中包含一个将线程创建抽象化的ThreadFactory接口。利用该接口,我们可以将以Runnable作为传入参数并通过new创建Thread实例的处理隐藏在ThreadFactory内部。

默认的ThreadFactory对象是通过Executors.defaultThreadFactory方法获取的。

public static void main(String[] args){

     ThreadFactory factory = Executors.defaultThreadFactory();

     factory.newThread(new Printer()).start();

     for(int i=0;i<100;i++){

          System.out.println("hello!");

     }

}

【线程的暂停】

线程Thread类中的sleep方法能够暂停线程运行,sleep是Thread类的静态方法。

public static void main(String[] args){

     for(int i=0;i<100;i++){

          System.out.println("hello!");

          try{

              Thread.sleep(1000);

          }catch(InterruptedException e){

          }

     }

}

【线程的互斥处理】

多线程程序的各个线程都是自由运行的,所以他们有时就会同时操作同一个实例。这在某些情况下会引发问题。

java使用关键字synchronized来执行线程的互斥处理。

【synchronized方法】

如果声明一个方法是,在前面加上关键字synchronized,那么这个方法就 只能由一个线程运行。称为同步方法。

public class Bank {

     private int money;

     private String name;

     

     public Bank(String name, int money){

          this.name = name;

          this.money = money;

     }

     //存款

     public synchronized void deposit(int m){

          money+=m;

     }

     //取款

     public synchronized boolean withdraw(int m){

          if(money>=m) {

              money-=m;

              return true;//取款成功

          } else {

              return false;//余额不足

          }

     }

     public String getName(){

          return name;

     }

}

【线程的协作】

如果有一个线程正在运行synchronized方法,那么其他线程就无法再运行这个方法了。这是简单的互斥处理。

假如我们现在想执行更加精确的控制,而不是单纯地等待其他线程运行终止,例如下面这样的控制:

    如果空间为空则写入数据;如果非空则一直等待到变空为止

    空间已为空时,通知正在等待的线程。

Java提供了用于执行线程控制的wait方法、notify方法和notifyAll方法。wait是让线程等待的方法,而notify和notifyAll是唤醒等待中的线程的方法。

【等待队列——线程休息室】

所有实例都拥有一个等待队列,它是在实例的wait方法执行后停止操作的线程的队列。

在执行wait方法后,线程便会暂停操作,进入等待队列这个休息室。除非发生下列某一情况,否则线程会一直在等待队列中休眠:

    1.有其他线程的notify方法唤醒线程

    2.有其他线程的notifyAll方法唤醒线程

    3.有其他线程的interrupt方法唤醒线程

    4.wait方法超时

若要执行wait方法,线程必须持有锁。但如果线程进入等待队列,便会释放其实例的锁。

【习题1 阅读下面内容,叙述正确请打√,错误请打×】

1.在Java程序中,至少有一个线程在运行。(√)

2.调用Thread类的run方法后,新的线程就会启动。(×)

答:启动新线程的并不是run方法,而是start方法。

3.start方法和run方法声明在Runnable接口中。(×)

答:只有run方法声明在Runnable接口中。

4有时多个线程会调用同一个实例的方法。(√)

答:正因为如此,我们才需要执行线程的互斥处理。

5.有时多个线程会调用Thread类的一个实例的方法。(√)

答:虽说是Thread类的实例方法,但与其他类的实例方法并没有什么不同,所以也会被多个线程调用。

6.sleep方法执行后,在指定的时间所有的线程都会暂停。(×)

答:暂停的只是调用了sleep方法的线程。

7.某个线程在运行synchronized方法时,其他所有线程都会停止运行。(×)

答:停止运行的只是想要获取同一个实例的锁的线程。

8.执行sleep方法后的线程仅在指定时间内待在等待队列中。(×)

答:执行sleep方法后的线程并不会进入等待队列。只有在wait方法后,线程才会进入等待队列。

9.wait方法的调用语句必须写在synchronized方法中。(×)

答:synchronized方法或synchronized代码块。只要执行wait方法的线程在执行时获得了对象实例的锁即可。

10.notifyAll方法是java.lang.Object类的实例方法。(√)

【习题2】

当下面的程序运行时,程序会在输出1000个“*”后,再输出1000个“+”。请问,为什么输出结果并不是“*”和“+”交错混杂的呢?

public class main{

    public static void main(String[] args){

        new PrintThread("*").run();

        new PrintThread("+").run();

    }

}

public class printThread extends Thread{

    private String message;

    public printThread(String message){

        this.message=message;

    }

    public void run(){

        for(int i=0;i<1000;i++){

            System.out.print(message);

        }

    }

}

答:因为主线程调用的不是start方法,而是run方法。new PrintThread("*").run();这条语句会创建PrintThread类的实例,并执行该实例的run方法。但执行run方法的并不是新线程,而是主线程。当输出100个“*”之后,下面的语句才会被执行。new PrintThread("+").run();

最终,所有的输出都是由主线程这一个线程来执行的,也就是说,这个程序其实是一个单线程程序。

//创建实例

MyThread t=new MyThread();

//启动线程

t.start();

//暂停已启动的线程

try{

    t.sleep(1000);

}catch(InterruptedException e){

}

【习题3】

某人写了如下代码,想让启动的线程暂停约1秒。但这个代码是错误的,为什么呢?假设下面的MyThread就是代码清单I1-2中声明的那个类。

答:这是因为执行t.sleep(1000);时暂停的并不是与t相关的那个线程,而是执行这条语句的线程。

t.sleep(1000);

上面这条语句调用出来的并不是t的实例方法,而是Thread的静态方法。也就是说,这等同于执行下面这条语句。

Thread.sleep(1000);

当想要暂停新启动的线程时,我们可以在MyThread类的run方法中调用sleep方法。

【习题4】

假设存在一个如下声明的Something类,变量x,y表示Something类的两个不同实例。请判断下列组合是否允许多个线程同时运行,允许请画√,否则请画×。

public classSomething {

    public void iA(){ }

    public void iB(){ }

    public synchronized void isSyncA(){ }

    public synchronized void isSyncB(){ }

    public static void cA(){ }

    public static void cB(){ }

    public static synchronized void cSyncA(){ }

    public static synchronized void cSyncB(){ }

}

√(1)x.iA(); 与 x.iA();

答:非synchronized方法可在任意时间多个线程运行。

√(2)x.iA(); 与 x.iB();

答:非synchronized方法可在任意时间多个线程运行。

√(3)x.iA(); 与 x.iSyncA();

答:非synchronized方法可在任意时间多个线程运行,即使存在正在运行其他的synchronized方法的线程,非synchronized方法也任然可以由多个线程运行。

×(4)x.iSyncA(); 与 x.iSyncA();

答:同一个实例的synchronized实例方法同时只能由一个线程运行。

×(5)x.iSyncA(); 与 x.iSyncA();

答:同一个实例的synchronized实例方法同时只能由一个线程运行。

√(6)x.iSyncA(); 与 y.iSyncA();

答:实例不同,锁也就不同,所以就算是synchronized实例方法,也可以由多个线程同时运行。

√(7)x.iSyncA(); 与 x.iSyncB();

答:实例不同,锁也就不同,所以就算是synchronized实例方法,也可以由多个线程同时运行。

√(8)x.iSyncA(); 与 Something.cA();

答:静态方法本来就不是synchronized方法,因此可以同时运行。

√(9)x.iSyncA(); 与 Something.cSyncA();

答:synchronized实例方法和synchronized静态方法的锁不同,所以可以由多个线程同时运行。

×(10)Something.cSyncA(); 与 Something.cSyncA();

答:synchronized静态方法不可以有多个线程同时运行。

×(11)Something.cSyncA(); 与 Something.cSyncB();

答:synchronized静态方法不可以有多个线程同时运行。

×(12)x.cSyncA(); 与 y.cSyncB();

答:synchronized静态方法不可以有多个线程同时运行。

猜你喜欢

转载自blog.csdn.net/u012530451/article/details/82120126
今日推荐