Java多线程记录(包含案例)

线程是程序的执行单元,执行路径。是程序使用CPU的最基本单位

多线程 -- 程序有多条执行路径,提高应用进程的使用率

进程中线程越多,抢到CPU执行权概率越高。线程的执行有随机性

 

Java程序的运行原理:

由Java命令启动JVM,JVM启动就相当于启动了一个进程,接着由该程序创建了一个主线程去调用main方法

JVM的启动是多线程的,因为垃圾回收线程也要启动,否则容易出现内存溢出。(GC线程和主线程)

 

Java多线程程序:

方法1:继承Thread类,并重写run()方法,创建对象,执行对象。

  run()方法: Thread类中的run()用来包含那些被线程执行的代码(封装被线程执行的代码)(不单独调用run方法(直接调用是普通方法),调用start())

  start()方法:首先启动了线程,然后再由JVM去调用该线程的run()方法

方法2:实现runnable接口

  A:自定义类实现runnable

  B:重写run()方法

  C:创建MyRunnable类的对象 

  D:创建Thread类的对象,把C步骤的对象作为构造参数传递

解决了java单继承的局限性,适合多个相同程序代码去处理同一个资源的情况,把线程相同的代码,数据有效分离

 

一些内建方法:

public final String getName()   //获取线程名称
public final String setName():    //设置线程名称
public static Thread currentThread():    //返回当前正在执行的线程对象

 

抢占式调度模型(Java):优先让优先级高的线程使用CPU

public final int getPriority():   //返回线程对象的优先级
public final void setPriority(): //设置线程优先级

优先级范围1-10,默认为5,最低为1,最高为5

 

线程控制:

public static void sleep(long millis)    //线程休眠
public final void join():   //线程加入   (等待线程终止)
public static void yield():  //线程礼让 暂停当前正在执行的线程对象,并执行其他线程
//让多个线程的执行更加和谐,但不能保证
public final void setDaemon(boolean on): //设置守护线程
//当正在运行的线程都是守护线程时,JVM退出,该方法必须在启动线程前调用
public void interrupt(): //中断线程,把线程的状态终止,并抛出一个InterruptException

 

线程的生命周期:(图解)

 

线程同步:synchronized关键字 

将多条语句操作的共享数据代码包成一个整体,让某个线程在执行的时候别人不执行

前提:多个线程;  注意:多个线程使用同一个锁对象

优点:解决了多线程的安全问题

缺点:当线程相当时,因为每个线程都会去判断同步上的锁,很耗费资源,无形中降低了程序的运行效率,容易产生死锁

使用同步代码块或者同步方法

 

死锁:

死锁代码举例:

1 public class MyLock {
2     // 创建两把锁对象
3     public static final Object objA = new Object();
4     public static final Object objB = new Object();
5 }
 1 public class DeadLock extends Thread {
 2 
 3     private boolean flag;
 4 
 5     public DeadLock(boolean flag) {
 6         this.flag = flag;
 7     }
 8 
 9     @Override
10     public void run() {
11         if (flag) {
12             synchronized (MyLock.objA) {
13                 System.out.println("if objA");
14                 synchronized (MyLock.objB) {
15                     System.out.println("if objB");
16                 }
17             }
18         } else {
19             synchronized (MyLock.objB) {
20                 System.out.println("else objB");
21                 synchronized (MyLock.objA) {
22                     System.out.println("else objA");
23                 }
24             }
25         }
26     }
27 }
1 public class DieLockDemo {
2     public static void main(String[] args) {
3         DeadLock dl1 = new DeadLock(true);
4         DeadLock dl2 = new DeadLock(false);
5 
6         dl1.start();
7         dl2.start();
8     }
9 }

 

等待唤醒机制:

Object类提供三个方法:

wait() :等待

notify():唤醒单个线程

notifyAll():唤醒所有线程

为什么不在Thread类中: 因为这些方法必须通过锁对象调用,而锁对象可以是任意锁对象,所以定义在object类中

 

Semaphore信号量:

信号量(Semaphore),又被称为信号灯,在多线程环境下用于协调各个线程, 以保证它们能够正确、合理的使用公共资源。信号量维护了一个许可集,我们在初始化Semaphore时需要为这个许可集传入一个数量值,该数量值代表同一时间能访问共享资源的线程数量。

线程可以通过acquire()方法获取到一个许可,然后对共享资源进行操作,注意如果许可集已分配完了,那么线程将进入等待状态,直到其他线程释放许可才有机会再获取许可,线程释放一个许可通过release()方法完成,"许可"将被归还给Semaphore。

 

多线程案例:

电影院卖票:电影院一共有100张票,有三个窗口卖票,模拟电影院售票:

使用同步代码块:

public class SellTicket implements Runnable {
    // 定义100张票
    private int tickets = 100;
    //创建锁对象
    private Object obj = new Object();
    
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()
                            + "正在出售第" + (tickets--) + "张票");
                }
            }
        }
    }
}
public class SellTicketDemo {
    public static void main(String[] args) {
        // 创建资源对象
        SellTicket st = new SellTicket();

        // 创建三个线程对象
        Thread t1 = new Thread(st, "窗口1");
        Thread t2 = new Thread(st, "窗口2");
        Thread t3 = new Thread(st, "窗口3");

        // 启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

使用同步方法:

 1 package lab2;
 2 
 3 public class SellTicket implements Runnable {
 4 
 5     // 定义100张票
 6     private static int tickets = 100;
 7 
 8     @Override
 9     public void run() {
10         while (true) {
11             sellTicket();
12         }
13     }
14     
15      private synchronized void sellTicket() {
16             if (tickets > 0) {
17             try {
18                     Thread.sleep(100);
19             } catch (InterruptedException e) {
20                     e.printStackTrace();
21             }
22             System.out.println(Thread.currentThread().getName()
23                         + "正在出售第" + (tickets--) + "张票 ");
24             }
25     }
26 }

使用锁方法:

 1 import java.util.concurrent.locks.Lock;
 2 import java.util.concurrent.locks.ReentrantLock;
 3 
 4 public class SellTicket implements Runnable {
 5 
 6     // 定义票
 7     private int tickets = 100;
 8 
 9     // 定义锁对象
10     private Lock lock = new ReentrantLock();
11 
12     @Override
13     public void run() {
14         while (true) {
15             try {
16                 // 加锁
17                 lock.lock();
18                 if (tickets > 0) {
19                     try {
20                         Thread.sleep(100);
21                     } catch (InterruptedException e) {
22                         e.printStackTrace();
23                     }
24                     System.out.println(Thread.currentThread().getName()
25                             + "正在出售第" + (tickets--) + "张票");
26                 }
27             } finally {
28                 // 释放锁
29                 lock.unlock();
30             }
31         }
32     }
33 
34 }

 

Ornamental Garden Problem:(使用Bakery class) 花园有两个旋转门,可进可出,监控花园内的人数

volatile关键字: Specifying a variable as volatile instructs the compiler to load and store the value of the variable at each use.   另外更多说明可以参考:https://www.cnblogs.com/daxin/p/3364014.html

Bakery Algorithm面包店算法

Bakery.java

 1 package lab2;
 2 
 3 public class Bakery {
 4     public int nThreads;
 5     
 6     private volatile int[] ticket;
 7     private volatile boolean[] choosing;
 8     
 9     Bakery(int nTh) {
10         nThreads = nTh;
11         ticket = new int[nThreads];
12         choosing = new boolean[nThreads];
13         
14         for (int i = 0; i < nThreads; i++) {
15             System.out.println(i + ticket[1]);
16             ticket[i] = 0;
17             choosing[i] = false;
18         }
19     }
20     
21     private int getNextNumber() {
22         int largest = 0;
23         for (int i = 0; i < nThreads; i++) {
24             if (ticket[i] > largest)
25                 largest = ticket[i];
26         }
27         return largest + 1;
28     }
29     
30     private boolean isFavouredThread(int i, int j) {
31         if ((ticket[i] == 0) || (ticket[i] > ticket[j]))
32             return false;
33         else {
34             if (ticket[i] < ticket[j])
35                 return true;
36             else
37                 return (i < j);
38         }
39     }
40 
41     void wantToEnterCS(int threadID) {
42         choosing[threadID] = true;
43         ticket[threadID] = getNextNumber();
44         choosing[threadID] = false;
45         
46         for (int otherThread = 0; otherThread < nThreads; otherThread++) {
47             while(choosing[otherThread]) {
48                 // busy-wait
49             }
50             // Break any ties between threads
51             while (isFavouredThread(otherThread,threadID)) {
52                 // busy-wait
53             }
54         }
55     }
56 
57     void exitCS(int threadID) {
58         // Leave critical section
59         ticket[threadID] = 0;
60     }
61 }

Couter.java

 1 package lab2;
 2 
 3 public class Counter {
 4     volatile int value = 0;
 5 
 6     Counter() {
 7         System.out.println("TOTAL: " + value);
 8     }
 9 
10     boolean increment() {
11         int temp = value;   //read[v]
12         Simulate.HWinterrupt();
13         value = temp + 1;       //write[v+1]
14         System.out.println("TOTAL: " + value);
15             return true;
16     }
17 
18     boolean decrement() {
19         int temp = value;   //read[v]
20         Simulate.HWinterrupt();
21             if (temp == 0) return false;
22         Simulate.HWinterrupt();
23         value = temp - 1;       //write[v+1]
24         System.out.println("TOTAL: " + value);
25             return true;
26     }
27 }
28 
29 class Simulate {
30     public static void HWinterrupt() {
31         if (Math.random() < 0.5)
32            try{
33                Thread.sleep(200);
34            } catch(InterruptedException ie) {};
35     }
36 }

Turnstile.java

 1 package lab2;
 2 
 3 /* Notes:
 4  * No modifications need be made to the bakery class.  Instead, the turnstile
 5  * class and counter classes are adjusted to allow for "exit" turnstiles as well
 6  * as entrance turnstiles.  This solution incorporates a system to prevent the
 7  * number of people in the gardens from being negative, so the sleep call in
 8  * this class can be commented out.  Study the code in the Counter and Turnstile
 9  * classes to see how how this works.
10  * */
11 
12 public class Gardens {
13      static Turnstile west;
14      static Turnstile east;
15      static Turnstile westExit;
16      static Turnstile eastExit;
17      static Counter people;
18      static Bakery bakery;
19      
20     public static void main(String[] args) {
21         people = new Counter();
22         bakery = new Bakery(4);
23          
24          west = new Turnstile(0, true, people, bakery);
25          east = new Turnstile(1, true, people, bakery);
26 
27          westExit = new Turnstile(2, false, people, bakery);
28          eastExit = new Turnstile(3, false, people, bakery);
29          
30          west.start();
31          east.start();
32 
33         /* 
34         try {
35             Thread.sleep(5000);
36         } catch (InterruptedException e) {
37         }
38         */
39 
40         westExit.start();
41         eastExit.start();
42     }
43 }

Garden.java

 1 package lab2;
 2 
 3 public class Gardens {
 4      static Turnstile west;
 5      static Turnstile east;
 6      static Turnstile westExit;
 7      static Turnstile eastExit;
 8      static Counter people;
 9      static Bakery bakery;
10      
11     public static void main(String[] args) {
12         people = new Counter();
13         bakery = new Bakery(4);
14          
15          west = new Turnstile(0, true, people, bakery);
16          east = new Turnstile(1, true, people, bakery);
17 
18          westExit = new Turnstile(2, false, people, bakery);
19          eastExit = new Turnstile(3, false, people, bakery);
20          
21          west.start();
22          east.start();
23 
24         /* 
25         try {
26             Thread.sleep(5000);
27         } catch (InterruptedException e) {
28         }
29         */
30 
31         westExit.start();
32         eastExit.start();
33     }
34 }

 

生产者消费者,操作列表:

Buffer.java

 1 import java.util.LinkedList;
 2 import java.util.NoSuchElementException;
 3 
 4 class Buffer {
 5     LinkedList<Integer> queue = new LinkedList<Integer>();
 6     
 7     public synchronized void write(int i) {
 8         queue.add(i);
 9     }
10 
11     public synchronized int read() {
12         try {
13             return queue.removeFirst();
14         } catch(NoSuchElementException e) {
15             // the buffer is empty!?
16             return -1;
17         }
18     }
19 }

Producer.java

 1 class Producer implements Runnable {
 2     Buffer buffer;
 3 
 4     public Producer(Buffer b) {
 5         buffer = b;
 6     }
 7 
 8     public void run() {
 9         for(int i = 0; i < 20; i++) {
10             buffer.write(i);
11             System.out.println("Thread " + Thread.currentThread().getId() +
12                 " writes " + i);
13         }
14     }
15 }

Consumer.java

 1 class Consumer implements Runnable {
 2     Buffer buffer;
 3 
 4     public Consumer(Buffer b) {
 5         buffer = b;
 6     }
 7 
 8     public void run() {
 9         for(int i = 0; i < 10; i++) {
10             try {
11                 Thread.sleep(10);
12             } catch (InterruptedException e) {
13             }
14             int x = buffer.read();
15             System.err.println("Thread " + Thread.currentThread().getId() +
16                 " reads " + x);
17         }
18     }
19 }

InfBuffer.java

 1 public class InfBuffer {
 2     public static void main(String args[]) {
 3         Buffer b = new Buffer();
 4         Consumer c1 = new Consumer(b);
 5         Consumer c2 = new Consumer(b);
 6         Producer p = new Producer(b);
 7         (new Thread(c1)).start();
 8         (new Thread(c2)).start();
 9         (new Thread(p)).start();
10     }
11 }

 

生产者消费者,修改学生姓名和年龄并获取:(只能一打一大片,无法实现生产者生产后等待消费者消费后再产生)

1 public class Student {
2     String name;
3     int age;
4 }
 1 public class SetThread implements Runnable {
 2 
 3     private Student s;
 4     private int x = 0;
 5 
 6     public SetThread(Student s) {
 7         this.s = s;
 8     }
 9 
10     @Override
11     public void run() {
12         while (true) {
13             synchronized (s) {
14                 if (x % 2 == 0) {
15                     s.name = "Student1";
16                     s.age = 27;
17                 } else {
18                     s.name = "Student2"; 
19                     s.age = 30;
20                 }
21                 x++;
22             }
23         }
24     }
25 }
 1 public class GetThread implements Runnable {
 2     private Student s;
 3 
 4     public GetThread(Student s) {
 5         this.s = s;
 6     }
 7 
 8     @Override
 9     public void run() {
10         while (true) {
11             synchronized (s) {
12                 System.out.println(s.name + "---" + s.age);
13             }
14         }
15     }
16 }
 1 public class StudentDemo {
 2     public static void main(String[] args) {
 3         //创建资源
 4         Student s = new Student();
 5         
 6         //设置和获取的类
 7         SetThread st = new SetThread(s);
 8         GetThread gt = new GetThread(s);
 9 
10         //线程类
11         Thread t1 = new Thread(st);
12         Thread t2 = new Thread(gt);
13 
14         //启动线程
15         t1.start();
16         t2.start();
17     }
18 }

 

生产者消费者,修改学生姓名和年龄并获取:(使用等待唤醒机制改进)

1 public class Student {
2     String name;
3     int age;
4     boolean flag;  //添加一个标记,默认为false,表示生产者是否生成值
5 }
 1 public class SetThread implements Runnable {
 2 
 3     private Student s;
 4     private int x = 0;
 5 
 6     public SetThread(Student s) {
 7         this.s = s;
 8     }
 9 
10     @Override
11     public void run() {
12         while (true) {
13             synchronized (s) {
14                 //判断有没有
15                 if(s.flag) {
16                     try {
17                         s.wait();
18                     } catch (InterruptedException e) {
19                         e.printStackTrace();
20                     }
21                 } 
22                 
23                 if (x % 2 == 0) {
24                     s.name = "Student1";
25                     s.age = 27;
26                 } else {
27                     s.name = "Student2"; 
28                     s.age = 30;
29                 }
30                 x++;
31                 
32                 //修改标记
33                 s.flag = true;
34                 //唤醒线程
35                 s.notify();
36             }
37         }
38     }
49 }
 1 public class GetThread implements Runnable {
 2     private Student s;
 3 
 4     public GetThread(Student s) {
 5         this.s = s;
 6     }
 7 
 8     @Override
 9     public void run() {
10         while (true) {
11             synchronized (s) {
12                 if (!s.flag) {
13                     try {
14                         s.wait();
15                     } catch (InterruptedException e) {
16                         e.printStackTrace();
17                     }
18                 }
19                 System.out.println(s.name + "---" + s.age);
20                 
21                 //修改标记
22                 s.flag = false;
23                 //唤醒线程
24                 s.notify();
25                 
26             }
27         }
28     }
29 }
public class StudentDemo {    //同上
    public static void main(String[] args) {
        //创建资源
        Student s = new Student();
        
        //设置和获取的类
        SetThread st = new SetThread(s);
        GetThread gt = new GetThread(s);

        //线程类
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(gt);

        //启动线程
        t1.start();
        t2.start();
    }
}

 

厕所排队(使用信号量)

 1 import java.util.Random;
 2 import java.util.concurrent.Semaphore;
 3 
 4 class Wc extends Thread {
 5     private String name;
 6     private Semaphore wc;
 7     
 8     public Wc(String name, Semaphore wc) {
 9         this.name = name;
10         this.wc = wc;
11     }
12     
13     @Override
14     public void run() {
15         int availablePermit = wc.availablePermits();
16         if (availablePermit > 0) {
17             System.out.println(name+",好开心啊,我终于有坑了");
18         }else {
19             System.out.println(name+"怎么没有坑了。。。");
20         }
21         
22         try {
23             wc.acquire();
24             System.out.println(name+",好开心啊,我终于抢到了!");
25             Thread.sleep(new Random().nextInt(1000));
26             System.out.println(name+",好爽啊,终于上完了!");
27             wc.release();
28         } catch (InterruptedException e) {
29 
30         }
31     }
32 }
33 
34 public class Demo {
35     public static void main(String[] args) {
36         Semaphore semaphore = new Semaphore(3);
37         
38         for (int i = 0; i < 10; i++) {
39             Wc wc = new Wc("第"+i+"个人", semaphore);
40             wc.start();
41         }
42     }
43 }

 

The Sleeping Barber (生产者消费者,信号量)

 1 import java.util.concurrent.Semaphore;
 2 
 3 public class Barber implements Runnable{
 4 
 5     Semaphore customerWaiting, seats, barberSleeping;
 6     
 7     Barber(Semaphore customerWaiting, Semaphore seats, Semaphore barberSleeping) {
 8         this.customerWaiting = customerWaiting;
 9         this.seats = seats;
10         this.barberSleeping = barberSleeping;
11     }
12     
13     public void run() {
14         
15         while (true){
16             
17             // Get a customer, sleep otherwise
18             try {
19                 customerWaiting.acquire();
20             } catch (InterruptedException e) {}
21 
22             // Cut the hair of the customer    
23             System.out.println("Cutting Hair");
24             barberSleeping.release();            
25             
26         }
27     }
28 }
 1 import java.util.concurrent.Semaphore;
 2 
 3 public class Customer implements Runnable{
 4 
 5     Semaphore customerWaiting, seats, barberSleeping;
 6 
 7     boolean cut = false;
 8     Customer(Semaphore customerWaiting, Semaphore seats, Semaphore barberSleeping) {
 9         this.customerWaiting = customerWaiting;
10         this.seats = seats;
11         this.barberSleeping = barberSleeping;
12 
13     }
14     
15     public void run() {
16         while (!cut) {
17             
18             // A random delay
19             // Don't want all the threads trying at once!
20             try {
21                 Thread.sleep((long)(Math.random()*100));
22             } catch (InterruptedException e1) {}
23             
24             // Try to get a seat in the waiting room
25             try {
26                 seats.acquire();
27             } catch (InterruptedException e) {}
28             System.out.println(seats.availablePermits());
29 
30                 System.out.println(Thread.currentThread().getName()+" is sitting down");
31                 // Try and wake barber
32                 customerWaiting.release();
33 
34                 
35                 // Get hair cut
36                 try {
37                     barberSleeping.acquire();
38                 } catch (InterruptedException e) {}
39                 cut = true;
40                 seats.release();
41             
42         }
43         System.out.println(Thread.currentThread().getName()+" has had hair cut");        
44     }
45 }
 1 import java.util.concurrent.Semaphore;
 2 
 3 public class Runner {
 4 
 5     public static void main(String[] args) {
 6         
 7         Semaphore barberSleeping = new Semaphore(1);
 8         Semaphore customerWaiting = new Semaphore(1);
 9         try {
10             customerWaiting.acquire();
11             barberSleeping.acquire();
12         } catch (InterruptedException e) {
13         }
14         
15         Semaphore seats = new Semaphore(3);
16         
17         Barber bar = new Barber(customerWaiting,seats,barberSleeping);
18         Thread bThread = new Thread(bar);
19         bThread.start();
20         
21         int nCust = 30;
22         Customer customers[] = new Customer[nCust];
23         Thread cThread[] = new Thread[nCust];
24         
25         for (int i=0;i<nCust;i++) {
26              customers[i] = new Customer(customerWaiting,seats,barberSleeping);
27              cThread[i] = new Thread(customers[i]);
28              cThread[i].start();
29         }
30     }
31 }

 

线程池:

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互,而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池

线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

 1 public class ExecutorsDemo {
 2     public static void main(String[] args) {
 3         // 创建一个线程池对象,控制要创建几个线程对象。
 4         // public static ExecutorService newFixedThreadPool(int nThreads)
 5         ExecutorService pool = Executors.newFixedThreadPool(2);        //直接通过类名调用
 6 
 7         // 可以执行Runnable对象或者Callable对象代表的线程
 8         pool.submit(new MyRunnable());
 9         pool.submit(new MyRunnable());
10 
11         //结束线程池
12         pool.shutdown();
13     }
14 }

使用Callable方法

public class MyCallable implements Callable {

    @Override
    public Object call() throws Exception {
        for (int x = 0; x < 100; x++) {
            System.out.println(Thread.currentThread().getName() + ":" + x);
        }
        return null;
    }

}
public class CallableDemo {
    public static void main(String[] args) {
        //创建线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(2);
        
        //可以执行Runnable对象或者Callable对象代表的线程
        pool.submit(new MyCallable());
        pool.submit(new MyCallable());
        
        //结束
        pool.shutdown();
    }
}

Callable带返回值:

 1 public class CallableDemo {
 2     public static void main(String[] args) throws InterruptedException, ExecutionException {
 3         // 创建线程池对象
 4         ExecutorService pool = Executors.newFixedThreadPool(2);
 5 
 6         // 可以执行Runnable对象或者Callable对象代表的线程
 7         Future<Integer> f1 = pool.submit(new MyCallable(100));
 8         Future<Integer> f2 = pool.submit(new MyCallable(200));
 9 
10         // V get()
11         Integer i1 = f1.get();
12         Integer i2 = f2.get();
13 
14         System.out.println(i1);
15         System.out.println(i2);
16 
17         // 结束
18         pool.shutdown();
19     }
20 }
 1 public class MyCallable implements Callable<Integer> {
 2 
 3     private int number;
 4 
 5     public MyCallable(int number) {
 6         this.number = number;
 7     }
 8 
 9     @Override
10     public Integer call() throws Exception {
11         int sum = 0;
12         for (int x = 1; x <= number; x++) {
13             sum += x;
14         }
15         return sum;
16     }
17 
18 }

 

匿名内部类实现多线程:

 1 public class ThreadDemo {
 2     public static void main(String[] args) {
 3         // 继承Thread类来实现多线程
 4         new Thread() {
 5             public void run() {
 6                 for (int x = 0; x < 100; x++) {
 7                     System.out.println(Thread.currentThread().getName() + ":"
 8                             + x);
 9                 }
10             }
11         }.start();
12 
13         // 实现Runnable接口来实现多线程
14         new Thread(new Runnable() {
15             @Override
16             public void run() {
17                 for (int x = 0; x < 100; x++) {
18                     System.out.println(Thread.currentThread().getName() + ":"
19                             + x);
20                 }
21             }
22         }) {
23         }.start();
24 
25         // 更有难度的
26         new Thread(new Runnable() {
27             @Override
28             public void run() {
29                 for (int x = 0; x < 100; x++) {
30                     System.out.println("hello" + ":" + x);
31                 }
32             }
33         }) {
34             public void run() {        //走的是这边的run方法
35                 for (int x = 0; x < 100; x++) {
36                     System.out.println("world" + ":" + x);
37                 }
38             }
39         }.start();
40     }
41 }

 

posted @ 2020-05-02 09:49  生于思考  阅读(465)  评论(0编辑  收藏  举报