Java 线程(多线程)详解

查看了许多书籍,网上的博客,现在我来说一下有关于我对线程的详解,有不对的欢迎指正。

一. 线程的生命周期:

    程序有自己的一个生命周期,线程也不例外,也有自己的生命周期。查看许多书籍或者网上资料,发现了一件很有趣的事情,那就是它们对线程的生命周期不是唯一。有两种或者以上的线程生命周期。

   第一种线程生命周期线程状态转换图:一共5个状态:新建,就绪,运行,阻塞和结束

 

 

                        图 1

 

第二种生命周期图:一共6个状态:New,Runnable,Blocked,Waiting,Timed Waiting,Terminated

 

 

                                                图 二

事实上,从java源码得知的是,线程的生命周期是属于第二种生命周期的,当然也不能说明除第二种生命周期,其余的生命周期是错的,只能说每种线程生命周期都是为了让学者更好的理解线程的状态。下面我说一下我对线程生命周期的理解,仅限参考:

1. 当Thread对象被创建时,该线程的状态便是新建状态

2. 当线程调用start()方法并调用时,该线程状态由新建状态进入了运行状态。

运行状态可以分为可运行状态和运行状态,对应图1的就绪状态和运行状态。当调用start()方法时,cpu要给予线程资源,当线程还未获得cpu资源,线程无法运行或者该线程获取了cpu资源,但未开始运行时,该线程状态称作为’可运行状态’,若线程获取cpu资源时,且线程运行线程执行体(run方法中的代码块),对应的状态便是’运行状态’。事实上,我们无法通过程序将线程’可运行状态’和’运行状态分离’出来,因此’可运行状态’对于开发者来说,是不存在的。

3. 当线程执行完毕后,线程便进入了结束的状态。当然,线程进入结束状态不仅仅只是线程执行完毕,还有线程执行过程中出现程序错误或者线程被程序强行结束(Thread.stop方法,可以让线程直接进入结束状态),这些都会让线程进入结束状态。

4. 除了上述说的3种状态,还有一种状态叫做阻塞状态。该状态是线程运行时,遇到指定的方法,该线程运行停止下来,那么该线程状态叫做阻塞状态。该状态是线程的重点,线程控制也是根据该状态而展开的。

 

二.线程控制:

说到线程控制,不得不提的便是多线程以及线程并发。何为多线程,当单一的线程运行时,称之为单线程,多条线程运行时,则称之为多线程。而线程并发,指的是同一时间,cpu同时处理多个线程。然而cpu并发线程的数量是(cpu物理线程数)有限的,而线程的创建则是无限的,当创建出来的线程数,已经超多了cpu物理线程数时,cpu本着雨露均沾的做法,先让给予对应cpu物理线程数的线程资源,并让这些线程运行一段时间,当运行时间到时,cpu会剥夺这些线程的资源,并给予另外一批对应cpu物理线程数的线程资源(该批线程中,可能有些线程和上一批是一样的),让其运行一段时间。周而复始,直到所有的线程运行结束。

 

class TestThread implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread()+" "+i);
        }
    }
}
public class Threads{
    public static void main(String[] args){
        TestThread testThread = new TestThread();
        for(int i=0;i<5;i++){
            Thread t = new Thread(testThread);
            t.start();
        }
    }
}    

上述代码我们创建了3个线程(3个线程所运行的线程执行体是一致的),并同时运行

结果一:

Thread[Thread-0,5,main] 0
Thread[Thread-3,5,main] 0
Thread[Thread-4,5,main] 0
Thread[Thread-4,5,main] 1
Thread[Thread-2,5,main] 0
Thread[Thread-1,5,main] 0
Thread[Thread-2,5,main] 1
Thread[Thread-4,5,main] 2
Thread[Thread-3,5,main] 1
Thread[Thread-0,5,main] 1

结果二:

Thread[Thread-1,5,main] 0
Thread[Thread-0,5,main] 0
Thread[Thread-1,5,main] 1
Thread[Thread-0,5,main] 1
Thread[Thread-1,5,main] 2
Thread[Thread-2,5,main] 0
Thread[Thread-0,5,main] 2
Thread[Thread-2,5,main] 1
Thread[Thread-1,5,main] 3
Thread[Thread-2,5,main] 2

我们根据结果一与结果二做对比,我们不难发现,运行出来的结果是不一致的,而这导致运行出来的结果不一致的原因则是因为cpu给予线程资源是根据cpu的’喜好’,同时该线程不是立刻执行完毕(由于这里执行的内容比较多,线程不能立刻执行完),而是执行到了一部分时,换成的另外一个线程执行。这也说明了cpu本着雨露均沾的做法,来实现多线程并发。

由于多线程的运行结果的不唯一,多线程的启动线程随意,会让数据发生丢失的可能,在程序上,再对要求结果要唯一的情况下,这是不允许的事情,因此我们要控制线程的状态,让运行结果变得唯一,让线程变得安全。以下是线程控制的几种类型:

  1. 改变线程优先度
  2. 线程停止运行方法
  3. 线程同步
  4. 线程通信

 

改变线程优先度:

当线程优先度最高时,cpu越喜欢,会优先给予资源,并让线程运行,当然当线程优先度相同时,cpu也只能随机选线程。对应方法:Thread.setPriorty(int),int参数范围为0-10,0优先度最低,1优先度最高

    我们根据上述代码进行优化,代码如下:

class TestThread implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread()+" "+i);
        }
    }
}
public class Threads{
    public static void main(String[] args){
        TestThread testThread = new TestThread();
        /**
         * 线程优先级设置
         * */
        Thread t_1 = new Thread(testThread);
        Thread t_2 = new Thread(testThread);
        Thread t_3 = new Thread(testThread);
        Thread t_4 = new Thread(testThread);
        Thread t_5 = new Thread(testThread);
        t_5.setPriority(10);
        t_5.start();t_1.start();t_2.start();t_3.start();t_4.start();
    }
}   

值得注意的是,尽管cpu会优先执行优先级别高的线程,但在程序中,代码会有执行的先后顺序,代码执行也需要时间,尽管这段时间我们不可能察觉,因为时间太短了,几毫秒的时间,但对于cpu来说,以及可以给予线程资源并让线程运行一段时间了,同时cpu虽然会优先执行优先级别高的线程,但也不是说一定会优先执行优先级别高的线程,只能说大概率的会优先执行,以下是运行结果前十行。

设置了优先级别高的线程,最大的优势在于,cpu会更倾向它,让它更快的完成运行

运行结果一:

Thread[Thread-4,10,main] 0
Thread[Thread-4,10,main] 1
Thread[Thread-0,5,main] 0
Thread[Thread-4,10,main] 2
Thread[Thread-0,5,main] 1
Thread[Thread-4,10,main] 3
Thread[Thread-0,5,main] 2
Thread[Thread-4,10,main] 4
Thread[Thread-0,5,main] 3
Thread[Thread-4,10,main] 5

运行结果二:

Thread[Thread-4,10,main] 0
Thread[Thread-1,5,main] 0
Thread[Thread-0,5,main] 0
Thread[Thread-3,5,main] 0
Thread[Thread-2,5,main] 0
Thread[Thread-1,5,main] 1
Thread[Thread-4,10,main] 1
Thread[Thread-1,5,main] 2
Thread[Thread-1,5,main] 3
Thread[Thread-2,5,main] 1

 

线程停止运行方法:

  1. Thread.sleep()方法,有Thread.sleep(long millis)和Thread.sleep(long millis,int nanos)选择,作用是让线程休息millis毫秒(+nanos毫微秒),并停止运行(剥夺cpu资源),当休息结束后回到线程可运行状态。该方法需要申明异常
  2. Thread.yield()方法,作用是让线程停一下,该线程从运行状态直接回到可运行状态,同时让同优先级别或以上的线程运行,并且cpu不取回该线程资源。由于cpu不取回资源,该线程会很快速进入运行状态。
  3. Thread.join()方法,该方法有Thread.join(),Thread.join(long millis)和Thread.join(long millis,int nanos)选择,作用是运行该程序的线程要等对应线程运行完毕后方可继续运行,有参数的情况是是等对应线程运行指定millis毫秒(+nanos毫微秒)后,便继续运行。      

 

Ps这里说明一下sleep方法与yield方法的区别:

  1. sleep执行后cpu找另外的线程运行,而yield执行后,是找优先度相同或以上的线程运行。
  2. sleep执行后,会进入阻塞的状态,而yield则是直接进入可运行状态
  3. sleep需要申明异常而yield不用
  4. sleep可移植性比yield好,因此尽可能的选择sleep方法
class TestThread implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread()+" "+i);
        }
    }
}
public class Threads{
    public static void main(String[] args){
        /**
         * 线程停止运行的方法
         * */
        System.out.println(Thread.currentThread()+"开始运行");
        TestThread test = new TestThread();
        Thread t_1 = new Thread(test);
        Thread t_2 = new Thread(test);
        Thread t_3 = new Thread(test);
        try{
            t_1.start();t_2.start();t_3.start();
            t_1.join();
        }catch(Exception e){}
        System.out.println(Thread.currentThread()+"停止运行");
    }
} 

上述代码中创建了3个线程,以及使用mian方法(主线程)。设置了主线程需要等待线程1运行结束后方法继续运行。大家可以去查看以下运行结果,会发现System.out.println(Thread.currentThread()+"停止运行")都是等待线程1打印完后打印的。

 

线程同步:

先看一下下述代码以及运行结果

/**
 * 未同步线程
 * */
class TestThreads implements Runnable{
    int n = 100;
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<50;i++){
            System.out.print(Thread.currentThread() +" "+ i);
            System.out.print("\t"+Thread.currentThread().getPriority());
            System.out.print("\t当前n为:"+n);
            n--;
            System.out.println("\t售后n为"+n);
        }
    }
}
public class Threads{
    public static void main(String[] args){
        /**
         * 未同步线程
         * */
        TestThreads testThreads = new TestThreads();
        Thread t_1 = new Thread(testThreads);
        Thread t_2 = new Thread(testThreads);
        Thread t_3 = new Thread(testThreads);
        t_1.start();t_2.start();t_3.start();        
        }
}

我们创建了3个线程,这三个线程都执行同一对象中的run方法,在run方法中,由四个打印语句。运行结果如下:

Thread[Thread-0,5,main] 0Thread[Thread-2,5,main] 0    5Thread[Thread-1,5,main] 0    当前n为:100    5    售后n为99
    5Thread[Thread-2,5,main] 1    5    当前n为:99    售后n为98
    当前n为:99    售后n为97
Thread[Thread-0,5,main] 1    5    当前n为:97    售后n为96
Thread[Thread-0,5,main] 2    5    当前n为:96    售后n为95
Thread[Thread-0,5,main] 3    5    当前n为:95    售后n为94
Thread[Thread-0,5,main] 4    5    当前n为:94    售后n为93
Thread[Thread-2,5,main] 2    当前n为:99    5Thread[Thread-0,5,main] 5    当前n为:92    售后n为92
    售后n为91
    5    当前n为:91Thread[Thread-2,5,main] 3    5    当前n为:90    售后n为89

我们可以看到运行的结果,非常混乱,并且该线程充满了不安全性。为了让线程有序,并且充满安全性,就需要用到线程的同步。

这里的线程同步,并非指多个线程并发同步执行。这里的同步,是指当多线程使用同一资源时,为了保证资源不混乱,并且有序的让多线程进行。线程同步的原理是,当多线程执行同一资源时,cpu只会让其中一个线程执行,当该线程执行完同步资源后,cpu会让另一个线程执行,周而复始,直到资源被用完,或者所有线程结束。你可以理解为,多人买车票的时候,需要排队买车票,可以以多线程当作多人,以车票数当作资源。网上有个网友说,线程同步,就是线程排队,实际上确实如此。而在程序中,如何能让多线程排队呢,答案是让每个线程带有锁与钥匙,但线程运行资源时,第一步时将资源锁住,由于其他线程的钥匙都打不开该线程的锁。那么其他的线程就会等待资源解锁的时候,而这时就只有该线程一个运行资源。但该线程运行完后,便会解锁,释放资源给其余的线程。周而复始,直到结束。

知道了线程同步的原理后,如何实现线程同步呢,有以下的方法:

     1.使用synchronized同步代码块或者synchronized同步方法

 将上述代码使用synchronized进行线程同步

/**
 * 同步线程
 * */
class SynThread implements Runnable{
    int n = 100;
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<50;i++){
            synchronized(this){             //将资源锁住,只有正确的锁才能开锁,这里的锁用的是对象
                if(n<=0){
                    Thread.currentThread().stop();
                }
                System.out.print(Thread.currentThread() +" "+ i);
                System.out.print("\t"+Thread.currentThread().getPriority());
                System.out.print("\t当前n为:"+n);
                n--;
                System.out.println("\t售后n为"+n);
            }
        }
    }
}
public class Threads{
    public static void main(String[] args){
        SynThread ft = new SynThread();
        for(int i=0 ;i<4;i++){
            Thread t = new Thread(ft);
            t.start();
        }
        }
}        

运行结果如下:

Thread[Thread-0,5,main] 0    5    当前n为:100    售后n为99
Thread[Thread-0,5,main] 1    5    当前n为:99    售后n为98
Thread[Thread-0,5,main] 2    5    当前n为:98    售后n为97
Thread[Thread-0,5,main] 3    5    当前n为:97    售后n为96
Thread[Thread-2,5,main] 0    5    当前n为:96    售后n为95
Thread[Thread-2,5,main] 1    5    当前n为:95    售后n为94
Thread[Thread-2,5,main] 2    5    当前n为:94    售后n为93
Thread[Thread-2,5,main] 3    5    当前n为:93    售后n为92
Thread[Thread-2,5,main] 4    5    当前n为:92    售后n为91
Thread[Thread-2,5,main] 5    5    当前n为:91    售后n为90
Thread[Thread-2,5,main] 6    5    当前n为:90    售后n为89
Thread[Thread-2,5,main] 7    5    当前n为:89    售后n为88
Thread[Thread-2,5,main] 8    5    当前n为:88    售后n为87
Thread[Thread-2,5,main] 9    5    当前n为:87    售后n为86
Thread[Thread-2,5,main] 10    5    当前n为:86    售后n为85
Thread[Thread-2,5,main] 11    5    当前n为:85    售后n为84
Thread[Thread-2,5,main] 12    5    当前n为:84    售后n为83
Thread[Thread-2,5,main] 13    5    当前n为:83    售后n为82
Thread[Thread-2,5,main] 14    5    当前n为:82    售后n为81
Thread[Thread-2,5,main] 15    5    当前n为:81    售后n为80
Thread[Thread-2,5,main] 16    5    当前n为:80    售后n为79
Thread[Thread-2,5,main] 17    5    当前n为:79    售后n为78
Thread[Thread-2,5,main] 18    5    当前n为:78    售后n为77
Thread[Thread-2,5,main] 19    5    当前n为:77    售后n为76
Thread[Thread-2,5,main] 20    5    当前n为:76    售后n为75
Thread[Thread-2,5,main] 21    5    当前n为:75    售后n为74
Thread[Thread-2,5,main] 22    5    当前n为:74    售后n为73
Thread[Thread-2,5,main] 23    5    当前n为:73    售后n为72
Thread[Thread-2,5,main] 24    5    当前n为:72    售后n为71
Thread[Thread-2,5,main] 25    5    当前n为:71    售后n为70
Thread[Thread-2,5,main] 26    5    当前n为:70    售后n为69
Thread[Thread-2,5,main] 27    5    当前n为:69    售后n为68
Thread[Thread-2,5,main] 28    5    当前n为:68    售后n为67
Thread[Thread-2,5,main] 29    5    当前n为:67    售后n为66
Thread[Thread-2,5,main] 30    5    当前n为:66    售后n为65
Thread[Thread-2,5,main] 31    5    当前n为:65    售后n为64
Thread[Thread-2,5,main] 32    5    当前n为:64    售后n为63
Thread[Thread-2,5,main] 33    5    当前n为:63    售后n为62
Thread[Thread-2,5,main] 34    5    当前n为:62    售后n为61
Thread[Thread-2,5,main] 35    5    当前n为:61    售后n为60
Thread[Thread-2,5,main] 36    5    当前n为:60    售后n为59
Thread[Thread-2,5,main] 37    5    当前n为:59    售后n为58
Thread[Thread-2,5,main] 38    5    当前n为:58    售后n为57
Thread[Thread-2,5,main] 39    5    当前n为:57    售后n为56
Thread[Thread-2,5,main] 40    5    当前n为:56    售后n为55
Thread[Thread-2,5,main] 41    5    当前n为:55    售后n为54
Thread[Thread-2,5,main] 42    5    当前n为:54    售后n为53
Thread[Thread-2,5,main] 43    5    当前n为:53    售后n为52
Thread[Thread-2,5,main] 44    5    当前n为:52    售后n为51
Thread[Thread-2,5,main] 45    5    当前n为:51    售后n为50
Thread[Thread-2,5,main] 46    5    当前n为:50    售后n为49
Thread[Thread-2,5,main] 47    5    当前n为:49    售后n为48
Thread[Thread-2,5,main] 48    5    当前n为:48    售后n为47
Thread[Thread-2,5,main] 49    5    当前n为:47    售后n为46
Thread[Thread-3,5,main] 0    5    当前n为:46    售后n为45
Thread[Thread-3,5,main] 1    5    当前n为:45    售后n为44
Thread[Thread-3,5,main] 2    5    当前n为:44    售后n为43
Thread[Thread-3,5,main] 3    5    当前n为:43    售后n为42
Thread[Thread-3,5,main] 4    5    当前n为:42    售后n为41
Thread[Thread-3,5,main] 5    5    当前n为:41    售后n为40
Thread[Thread-3,5,main] 6    5    当前n为:40    售后n为39
Thread[Thread-3,5,main] 7    5    当前n为:39    售后n为38
Thread[Thread-3,5,main] 8    5    当前n为:38    售后n为37
Thread[Thread-3,5,main] 9    5    当前n为:37    售后n为36
Thread[Thread-3,5,main] 10    5    当前n为:36    售后n为35
Thread[Thread-3,5,main] 11    5    当前n为:35    售后n为34
Thread[Thread-3,5,main] 12    5    当前n为:34    售后n为33
Thread[Thread-3,5,main] 13    5    当前n为:33    售后n为32
Thread[Thread-3,5,main] 14    5    当前n为:32    售后n为31
Thread[Thread-3,5,main] 15    5    当前n为:31    售后n为30
Thread[Thread-3,5,main] 16    5    当前n为:30    售后n为29
Thread[Thread-3,5,main] 17    5    当前n为:29    售后n为28
Thread[Thread-3,5,main] 18    5    当前n为:28    售后n为27
Thread[Thread-3,5,main] 19    5    当前n为:27    售后n为26
Thread[Thread-3,5,main] 20    5    当前n为:26    售后n为25
Thread[Thread-3,5,main] 21    5    当前n为:25    售后n为24
Thread[Thread-3,5,main] 22    5    当前n为:24    售后n为23
Thread[Thread-3,5,main] 23    5    当前n为:23    售后n为22
Thread[Thread-3,5,main] 24    5    当前n为:22    售后n为21
Thread[Thread-3,5,main] 25    5    当前n为:21    售后n为20
Thread[Thread-3,5,main] 26    5    当前n为:20    售后n为19
Thread[Thread-3,5,main] 27    5    当前n为:19    售后n为18
Thread[Thread-3,5,main] 28    5    当前n为:18    售后n为17
Thread[Thread-3,5,main] 29    5    当前n为:17    售后n为16
Thread[Thread-3,5,main] 30    5    当前n为:16    售后n为15
Thread[Thread-3,5,main] 31    5    当前n为:15    售后n为14
Thread[Thread-3,5,main] 32    5    当前n为:14    售后n为13
Thread[Thread-3,5,main] 33    5    当前n为:13    售后n为12
Thread[Thread-3,5,main] 34    5    当前n为:12    售后n为11
Thread[Thread-3,5,main] 35    5    当前n为:11    售后n为10
Thread[Thread-3,5,main] 36    5    当前n为:10    售后n为9
Thread[Thread-1,5,main] 0    5    当前n为:9    售后n为8
Thread[Thread-1,5,main] 1    5    当前n为:8    售后n为7
Thread[Thread-1,5,main] 2    5    当前n为:7    售后n为6
Thread[Thread-1,5,main] 3    5    当前n为:6    售后n为5
Thread[Thread-1,5,main] 4    5    当前n为:5    售后n为4
Thread[Thread-1,5,main] 5    5    当前n为:4    售后n为3
Thread[Thread-1,5,main] 6    5    当前n为:3    售后n为2
Thread[Thread-1,5,main] 7    5    当前n为:2    售后n为1
Thread[Thread-1,5,main] 8    5    当前n为:1    售后n为0
View Code

     

  2. 使用lock锁进行同步

/**
 * lock同步锁
 * */
class LockThread implements Runnable{
    LockTest lockTest = null;
    public LockThread(LockTest lockTest){
        this.lockTest = lockTest;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<100;i++){
            lockTest.sub();
        }
    }
}

class LockTest{
    int n = 100;
    private final ReentrantLock lock = new ReentrantLock();
    
    public void sub(){
        lock.lock();
        try{
            if(n>0){
                System.out.print(Thread.currentThread()+"操作");
                System.out.print("\t当前n数为:"+n);
                n--;
                System.out.println("\t 操作后n数为:"+n);
            }else{
                System.out.println(Thread.currentThread()+"结束");
                Thread.currentThread().stop();
            }
        }finally{
            lock.unlock();
        }
    }
}
public class Threads{
    public static void main(String[] args){
        LockTest lockTest = new LockTest();
        LockThread lockThread = new LockThread(lockTest);
        for(int i=0 ;i<4;i++){
            Thread s = new Thread(lockThread);
            s.start();
        }
    }
}

运行结果如下:

Thread[Thread-0,5,main]操作    当前n数为:100     操作后n数为:99
Thread[Thread-0,5,main]操作    当前n数为:99     操作后n数为:98
Thread[Thread-0,5,main]操作    当前n数为:98     操作后n数为:97
Thread[Thread-0,5,main]操作    当前n数为:97     操作后n数为:96
Thread[Thread-1,5,main]操作    当前n数为:96     操作后n数为:95
Thread[Thread-1,5,main]操作    当前n数为:95     操作后n数为:94
Thread[Thread-1,5,main]操作    当前n数为:94     操作后n数为:93
Thread[Thread-1,5,main]操作    当前n数为:93     操作后n数为:92
Thread[Thread-1,5,main]操作    当前n数为:92     操作后n数为:91
Thread[Thread-1,5,main]操作    当前n数为:91     操作后n数为:90
Thread[Thread-1,5,main]操作    当前n数为:90     操作后n数为:89
Thread[Thread-1,5,main]操作    当前n数为:89     操作后n数为:88
Thread[Thread-1,5,main]操作    当前n数为:88     操作后n数为:87
Thread[Thread-1,5,main]操作    当前n数为:87     操作后n数为:86
Thread[Thread-1,5,main]操作    当前n数为:86     操作后n数为:85
Thread[Thread-1,5,main]操作    当前n数为:85     操作后n数为:84
Thread[Thread-1,5,main]操作    当前n数为:84     操作后n数为:83
Thread[Thread-1,5,main]操作    当前n数为:83     操作后n数为:82
Thread[Thread-1,5,main]操作    当前n数为:82     操作后n数为:81
Thread[Thread-1,5,main]操作    当前n数为:81     操作后n数为:80
Thread[Thread-1,5,main]操作    当前n数为:80     操作后n数为:79
Thread[Thread-1,5,main]操作    当前n数为:79     操作后n数为:78
Thread[Thread-1,5,main]操作    当前n数为:78     操作后n数为:77
Thread[Thread-1,5,main]操作    当前n数为:77     操作后n数为:76
Thread[Thread-1,5,main]操作    当前n数为:76     操作后n数为:75
Thread[Thread-1,5,main]操作    当前n数为:75     操作后n数为:74
Thread[Thread-1,5,main]操作    当前n数为:74     操作后n数为:73
Thread[Thread-1,5,main]操作    当前n数为:73     操作后n数为:72
Thread[Thread-1,5,main]操作    当前n数为:72     操作后n数为:71
Thread[Thread-1,5,main]操作    当前n数为:71     操作后n数为:70
Thread[Thread-1,5,main]操作    当前n数为:70     操作后n数为:69
Thread[Thread-1,5,main]操作    当前n数为:69     操作后n数为:68
Thread[Thread-1,5,main]操作    当前n数为:68     操作后n数为:67
Thread[Thread-1,5,main]操作    当前n数为:67     操作后n数为:66
Thread[Thread-1,5,main]操作    当前n数为:66     操作后n数为:65
Thread[Thread-1,5,main]操作    当前n数为:65     操作后n数为:64
Thread[Thread-1,5,main]操作    当前n数为:64     操作后n数为:63
Thread[Thread-1,5,main]操作    当前n数为:63     操作后n数为:62
Thread[Thread-1,5,main]操作    当前n数为:62     操作后n数为:61
Thread[Thread-1,5,main]操作    当前n数为:61     操作后n数为:60
Thread[Thread-1,5,main]操作    当前n数为:60     操作后n数为:59
Thread[Thread-1,5,main]操作    当前n数为:59     操作后n数为:58
Thread[Thread-1,5,main]操作    当前n数为:58     操作后n数为:57
Thread[Thread-1,5,main]操作    当前n数为:57     操作后n数为:56
Thread[Thread-1,5,main]操作    当前n数为:56     操作后n数为:55
Thread[Thread-1,5,main]操作    当前n数为:55     操作后n数为:54
Thread[Thread-1,5,main]操作    当前n数为:54     操作后n数为:53
Thread[Thread-1,5,main]操作    当前n数为:53     操作后n数为:52
Thread[Thread-1,5,main]操作    当前n数为:52     操作后n数为:51
Thread[Thread-1,5,main]操作    当前n数为:51     操作后n数为:50
Thread[Thread-1,5,main]操作    当前n数为:50     操作后n数为:49
Thread[Thread-1,5,main]操作    当前n数为:49     操作后n数为:48
Thread[Thread-1,5,main]操作    当前n数为:48     操作后n数为:47
Thread[Thread-1,5,main]操作    当前n数为:47     操作后n数为:46
Thread[Thread-1,5,main]操作    当前n数为:46     操作后n数为:45
Thread[Thread-1,5,main]操作    当前n数为:45     操作后n数为:44
Thread[Thread-1,5,main]操作    当前n数为:44     操作后n数为:43
Thread[Thread-1,5,main]操作    当前n数为:43     操作后n数为:42
Thread[Thread-1,5,main]操作    当前n数为:42     操作后n数为:41
Thread[Thread-1,5,main]操作    当前n数为:41     操作后n数为:40
Thread[Thread-1,5,main]操作    当前n数为:40     操作后n数为:39
Thread[Thread-1,5,main]操作    当前n数为:39     操作后n数为:38
Thread[Thread-1,5,main]操作    当前n数为:38     操作后n数为:37
Thread[Thread-1,5,main]操作    当前n数为:37     操作后n数为:36
Thread[Thread-1,5,main]操作    当前n数为:36     操作后n数为:35
Thread[Thread-1,5,main]操作    当前n数为:35     操作后n数为:34
Thread[Thread-1,5,main]操作    当前n数为:34     操作后n数为:33
Thread[Thread-1,5,main]操作    当前n数为:33     操作后n数为:32
Thread[Thread-1,5,main]操作    当前n数为:32     操作后n数为:31
Thread[Thread-1,5,main]操作    当前n数为:31     操作后n数为:30
Thread[Thread-1,5,main]操作    当前n数为:30     操作后n数为:29
Thread[Thread-1,5,main]操作    当前n数为:29     操作后n数为:28
Thread[Thread-1,5,main]操作    当前n数为:28     操作后n数为:27
Thread[Thread-1,5,main]操作    当前n数为:27     操作后n数为:26
Thread[Thread-1,5,main]操作    当前n数为:26     操作后n数为:25
Thread[Thread-1,5,main]操作    当前n数为:25     操作后n数为:24
Thread[Thread-1,5,main]操作    当前n数为:24     操作后n数为:23
Thread[Thread-1,5,main]操作    当前n数为:23     操作后n数为:22
Thread[Thread-1,5,main]操作    当前n数为:22     操作后n数为:21
Thread[Thread-1,5,main]操作    当前n数为:21     操作后n数为:20
Thread[Thread-1,5,main]操作    当前n数为:20     操作后n数为:19
Thread[Thread-1,5,main]操作    当前n数为:19     操作后n数为:18
Thread[Thread-1,5,main]操作    当前n数为:18     操作后n数为:17
Thread[Thread-1,5,main]操作    当前n数为:17     操作后n数为:16
Thread[Thread-1,5,main]操作    当前n数为:16     操作后n数为:15
Thread[Thread-1,5,main]操作    当前n数为:15     操作后n数为:14
Thread[Thread-1,5,main]操作    当前n数为:14     操作后n数为:13
Thread[Thread-1,5,main]操作    当前n数为:13     操作后n数为:12
Thread[Thread-1,5,main]操作    当前n数为:12     操作后n数为:11
Thread[Thread-1,5,main]操作    当前n数为:11     操作后n数为:10
Thread[Thread-1,5,main]操作    当前n数为:10     操作后n数为:9
Thread[Thread-1,5,main]操作    当前n数为:9     操作后n数为:8
Thread[Thread-1,5,main]操作    当前n数为:8     操作后n数为:7
Thread[Thread-1,5,main]操作    当前n数为:7     操作后n数为:6
Thread[Thread-1,5,main]操作    当前n数为:6     操作后n数为:5
Thread[Thread-1,5,main]操作    当前n数为:5     操作后n数为:4
Thread[Thread-1,5,main]操作    当前n数为:4     操作后n数为:3
Thread[Thread-1,5,main]操作    当前n数为:3     操作后n数为:2
Thread[Thread-1,5,main]操作    当前n数为:2     操作后n数为:1
Thread[Thread-1,5,main]操作    当前n数为:1     操作后n数为:0
Thread[Thread-1,5,main]结束
Thread[Thread-2,5,main]结束
Thread[Thread-3,5,main]结束
Thread[Thread-0,5,main]结束
View Code

 

无论是使用方法一或者方法二,从运行的结果可以发现,结果都是有序运行的,而不想未同步时的顺序打乱。有关线程同步有以下需要注意的地方:

1. 实现线程同步的前提条件是多线程,并且线程执行内容要一致(引用同一线程targer对象)。若是单一线程,根本无需用到锁。

2. 同步的资源不一定放在run方法中,也可以放在其他的方法体里,让run方法调用该方法。

3. 线程同步,有可能会造成死锁,何为死锁,线程1需要线程2的锁放可进行运行,而线程2需要线程1的锁放可运行。两个线程都不让步,便会造成死锁。在程序中,由于资源被锁住的原因,线程进入阻塞状态,并且双方都不让步,便会导致了两个线程无限的等待下去。因此编写程序时应该避免死锁。

 

线程通信:

    当两个线程执行的内容不同,但相互间有联系,这便是线程的通信。要先实现线程通信,有以下的条件:

1. 必须有两个或以上不同的线程执行体,即run方法执行的内容不一致

2. 每个线程执行体必须符合线程同步。

线程通信最经典的例子便是生产者与消费者。生产者生产一件东西,而消费者消费东西。其执行顺序时,生产1,消费1,生产2,消费2的顺序。由于生产者与消费者不仅仅只是单一线程的生产与消费,因此生产者与消费者需要当作资源锁住。如何实现线程通信,有以下的方法:

1.使用synchronized与Object.wait(),notify(),notifyAll()方法实现

2.使用lock同步锁与Condition类实现

3.使用阻塞队列BlockingQueue实现

以下是代码:

建立一个生产者与消费者都会使用到的类Request

class Requests{
    private final Lock lock = new ReentrantLock();
    private final Condition cond = lock.newCondition();
    //创建长度为2的阻塞队列
    private final BlockingQueue aQueue = new ArrayBlockingQueue(2);
    String url;
    int paramentCount;
    boolean bFull,lFull = false;
    
    /**
     * 使用synchronized同步块或同步方法进行通讯
     * */
    public synchronized void set_1(String url,int paramentCount){
        if(bFull){
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        this.url = url;
        this.paramentCount = paramentCount;
        System.out.println("synchronized "+Thread.currentThread()+" 放入了"+url+"----->"+paramentCount);
        bFull = true;
        notify();
    }
    public synchronized void get_1(){
        if(!bFull){
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("synchronized"+Thread.currentThread()+url+"------->"+paramentCount);
        bFull = false;
        notify();
    }
    
    /**
     * 使用lock同步锁进行通讯
     * */
    public void set_2(String url,int paramentCount){
        lock.lock();
        try{
            if(lFull){
                try {
                    cond.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            this.url = url;
            this.paramentCount = paramentCount;
            System.out.println("lock  "+Thread.currentThread()+" 放入了"+url+"----->"+paramentCount);
            lFull = true;
            cond.signal();
        }finally{
            lock.unlock();
        }
    }
    public void get_2(){
        lock.lock();
        try{
            if(!lFull){
                try {
                    cond.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("lock  "+Thread.currentThread()+"取出来"+url+"------->"+paramentCount);
            lFull = false;
            cond.signal();
        }finally{
            lock.unlock();
        }
    }
    
    /**
     * 使用BlockingQueue进行通讯
     * */
    public void set_3(String url,int paramentCount){
        try {
            aQueue.put(url+"  "+paramentCount);
            System.out.println("lock  "+Thread.currentThread()+" 放入了数据");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public void get_3(){
        try {
            System.out.println("BlockingQueue  "+Thread.currentThread()+"取出来数据:"+aQueue.take());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

在Request中一个有6个方法,set_1,set_2,set_3,get_1,get_2,get3方法。其中,set_1与get_1使用的是的synchronized与Object.wait(),notify(),notifyAll()方法得线程通信,set_2与get_2是使用lock同步锁与Condition类实现的线程通信,set_3与get_3则是使用阻塞队列BlockingQueue实现的线程通信。

生产者类:

class Productor implements Runnable{
    Requests request = null;
    public Productor(Requests requests){
        request = requests;
        Thread t = new Thread(this);
        t.start();
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<10;i++){
            if(i%2 == 0){
                //使用Synchronized同步块或者同步方法
//                request.set_1("localhost", 1);
                //使用lock同步锁
//                request.set_2("localhost", 1);
                //使用BlockingQueue阻塞队列
                request.set_3("localhost", 1);
            }else{
                //使用Synchronized同步块或者同步方法
//                request.set_1("127.0.0.1", 3);
//                使用lock同步锁
//                request.set_2("127.0.0.1", 3);
                //使用BlockingQueue阻塞队列
                request.set_3("127.0.0.1", 3);
            }
        }
    }
}

消费者:

class Consumer implements Runnable{
    Requests request = null;
    public Consumer(Requests requests){
        request = requests;
        Thread t = new Thread(this);
        t.start();
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<10;i++){
            //使用Synchronized同步块或者同步方法
//            request.get_1();
            //使用lock同步锁
//            request.get_2();
            //使用BlockingQueue阻塞队列
            request.get_3();
        }
    }
}

使用生产者与消费者创建线程并运行:

public class Threads{
    public static void main(String[] args){
        Requests request = new Requests();
        Productor p = new Productor(request);
        Consumer c = new Consumer(request);
        }
}        

由于生产者与消费者都有三种实现线程通信的方法,需要注释另外两种,仅有一种线程通信方法运行结果如下:

方法一运行效果如下:

synchronized Thread[Thread-0,5,main] 放入了localhost----->1
synchronizedThread[Thread-1,5,main]localhost------->1
synchronized Thread[Thread-0,5,main] 放入了127.0.0.1----->3
synchronizedThread[Thread-1,5,main]127.0.0.1------->3
synchronized Thread[Thread-0,5,main] 放入了localhost----->1
synchronizedThread[Thread-1,5,main]localhost------->1
synchronized Thread[Thread-0,5,main] 放入了127.0.0.1----->3
synchronizedThread[Thread-1,5,main]127.0.0.1------->3
synchronized Thread[Thread-0,5,main] 放入了localhost----->1
synchronizedThread[Thread-1,5,main]localhost------->1
synchronized Thread[Thread-0,5,main] 放入了127.0.0.1----->3
synchronizedThread[Thread-1,5,main]127.0.0.1------->3
synchronized Thread[Thread-0,5,main] 放入了localhost----->1
synchronizedThread[Thread-1,5,main]localhost------->1
synchronized Thread[Thread-0,5,main] 放入了127.0.0.1----->3
synchronizedThread[Thread-1,5,main]127.0.0.1------->3
synchronized Thread[Thread-0,5,main] 放入了localhost----->1
synchronizedThread[Thread-1,5,main]localhost------->1
synchronized Thread[Thread-0,5,main] 放入了127.0.0.1----->3
synchronizedThread[Thread-1,5,main]127.0.0.1------->3
View Code

方法二运行效果如下:

lock  Thread[Thread-0,5,main] 放入了localhost----->1
lock  Thread[Thread-1,5,main]取出来localhost------->1
lock  Thread[Thread-0,5,main] 放入了127.0.0.1----->3
lock  Thread[Thread-1,5,main]取出来127.0.0.1------->3
lock  Thread[Thread-0,5,main] 放入了localhost----->1
lock  Thread[Thread-1,5,main]取出来localhost------->1
lock  Thread[Thread-0,5,main] 放入了127.0.0.1----->3
lock  Thread[Thread-1,5,main]取出来127.0.0.1------->3
lock  Thread[Thread-0,5,main] 放入了localhost----->1
lock  Thread[Thread-1,5,main]取出来localhost------->1
lock  Thread[Thread-0,5,main] 放入了127.0.0.1----->3
lock  Thread[Thread-1,5,main]取出来127.0.0.1------->3
lock  Thread[Thread-0,5,main] 放入了localhost----->1
lock  Thread[Thread-1,5,main]取出来localhost------->1
lock  Thread[Thread-0,5,main] 放入了127.0.0.1----->3
lock  Thread[Thread-1,5,main]取出来127.0.0.1------->3
lock  Thread[Thread-0,5,main] 放入了localhost----->1
lock  Thread[Thread-1,5,main]取出来localhost------->1
lock  Thread[Thread-0,5,main] 放入了127.0.0.1----->3
lock  Thread[Thread-1,5,main]取出来127.0.0.1------->3
View Code

方法三运行效果如下:

BlockingQueue  Thread[Thread-0,5,main] 放入了数据
BlockingQueue  Thread[Thread-0,5,main] 放入了数据
BlockingQueue  Thread[Thread-1,5,main]取出来数据:localhost  1
BlockingQueue  Thread[Thread-1,5,main]取出来数据:127.0.0.1  3
BlockingQueue  Thread[Thread-0,5,main] 放入了数据
BlockingQueue  Thread[Thread-1,5,main]取出来数据:localhost  1
BlockingQueue  Thread[Thread-0,5,main] 放入了数据
BlockingQueue  Thread[Thread-1,5,main]取出来数据:127.0.0.1  3
BlockingQueue  Thread[Thread-0,5,main] 放入了数据
BlockingQueue  Thread[Thread-1,5,main]取出来数据:localhost  1
BlockingQueue  Thread[Thread-0,5,main] 放入了数据
BlockingQueue  Thread[Thread-1,5,main]取出来数据:127.0.0.1  3
BlockingQueue  Thread[Thread-0,5,main] 放入了数据
BlockingQueue  Thread[Thread-0,5,main] 放入了数据
BlockingQueue  Thread[Thread-0,5,main] 放入了数据
BlockingQueue  Thread[Thread-1,5,main]取出来数据:localhost  1
BlockingQueue  Thread[Thread-1,5,main]取出来数据:127.0.0.1  3
BlockingQueue  Thread[Thread-0,5,main] 放入了数据
BlockingQueue  Thread[Thread-1,5,main]取出来数据:localhost  1
BlockingQueue  Thread[Thread-1,5,main]取出来数据:127.0.0.1  3
View Code

由于方法三实现的通信原理与方法一和方法二不同,因此不能像方法一与方法二那样一生产一消费,但也保证了消费前要生产。

 

全部代码:

class TestThread implements Runnable{
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread()+" "+i);
        }
    }
}

/**
 * 未同步线程
 * */
class TestThreads implements Runnable{
    int n = 100;
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<50;i++){
            System.out.print(Thread.currentThread() +" "+ i);
            System.out.print("\t"+Thread.currentThread().getPriority());
            System.out.print("\t当前n为:"+n);
            n--;
            System.out.println("\t售后n为"+n);
        }
    }
}

/**
 * 同步线程
 * */
class SynThread implements Runnable{
    int n = 100;
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<50;i++){
            synchronized(this){
                if(n<=0){
                    Thread.currentThread().stop();
                }
                System.out.print(Thread.currentThread() +" "+ i);
                System.out.print("\t"+Thread.currentThread().getPriority());
                System.out.print("\t当前n为:"+n);
                n--;
                System.out.println("\t售后n为"+n);
            }
        }
    }
}

/**
 * lock同步锁
 * */
class LockThread implements Runnable{
    LockTest lockTest = null;
    public LockThread(LockTest lockTest){
        this.lockTest = lockTest;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<100;i++){
            lockTest.sub();
        }
    }
}

class LockTest{
    int n = 100;
    private final ReentrantLock lock = new ReentrantLock();
    
    public void sub(){
        lock.lock();
        try{
            if(n>0){
                System.out.print(Thread.currentThread()+"操作");
                System.out.print("\t当前n数为:"+n);
                n--;
                System.out.println("\t 操作后n数为:"+n);
            }else{
                System.out.println(Thread.currentThread()+"结束");
                Thread.currentThread().stop();
            }
        }finally{
            lock.unlock();
        }
    }
}

/**
 * 线程通讯
 * */
class Productor implements Runnable{
    Requests request = null;
    public Productor(Requests requests){
        request = requests;
        Thread t = new Thread(this);
        t.start();
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<10;i++){
            if(i%2 == 0){
                //使用Synchronized同步块或者同步方法
//                request.set_1("localhost", 1);
                //使用lock同步锁
//                request.set_2("localhost", 1);
                //使用BlockingQueue阻塞队列
                request.set_3("localhost", 1);
            }else{
                //使用Synchronized同步块或者同步方法
//                request.set_1("127.0.0.1", 3);
//                使用lock同步锁
//                request.set_2("127.0.0.1", 3);
                //使用BlockingQueue阻塞队列
                request.set_3("127.0.0.1", 3);
            }
        }
    }
}

class Consumer implements Runnable{
    Requests request = null;
    public Consumer(Requests requests){
        request = requests;
        Thread t = new Thread(this);
        t.start();
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(int i=0;i<10;i++){
            //使用Synchronized同步块或者同步方法
//            request.get_1();
            //使用lock同步锁
//            request.get_2();
            //使用BlockingQueue阻塞队列
            request.get_3();
        }
    }
}
class Requests{
    private final Lock lock = new ReentrantLock();
    private final Condition cond = lock.newCondition();
    //创建长度为2的阻塞队列
    private final BlockingQueue aQueue = new ArrayBlockingQueue(2);
    String url;
    int paramentCount;
    boolean bFull,lFull = false;
    
    /**
     * 使用synchronized同步块或同步方法进行通讯
     * */
    public synchronized void set_1(String url,int paramentCount){
        if(bFull){
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        this.url = url;
        this.paramentCount = paramentCount;
        System.out.println("synchronized "+Thread.currentThread()+" 放入了"+url+"----->"+paramentCount);
        bFull = true;
        notify();
    }
    public synchronized void get_1(){
        if(!bFull){
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("synchronized"+Thread.currentThread()+url+"------->"+paramentCount);
        bFull = false;
        notify();
    }
    
    /**
     * 使用lock同步锁进行通讯
     * */
    public void set_2(String url,int paramentCount){
        lock.lock();
        try{
            if(lFull){
                try {
                    cond.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            this.url = url;
            this.paramentCount = paramentCount;
            System.out.println("lock  "+Thread.currentThread()+" 放入了"+url+"----->"+paramentCount);
            lFull = true;
            cond.signal();
        }finally{
            lock.unlock();
        }
    }
    public void get_2(){
        lock.lock();
        try{
            if(!lFull){
                try {
                    cond.await();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("lock  "+Thread.currentThread()+"取出来"+url+"------->"+paramentCount);
            lFull = false;
            cond.signal();
        }finally{
            lock.unlock();
        }
    }
    
    /**
     * 使用BlockingQueue进行通讯
     * */
    public void set_3(String url,int paramentCount){
        try {
            aQueue.put(url+"  "+paramentCount);
            System.out.println("BlockingQueue  "+Thread.currentThread()+" 放入了数据");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public void get_3(){
        try {
            System.out.println("BlockingQueue  "+Thread.currentThread()+"取出来数据:"+aQueue.take());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

public class Threads{
    public static void main(String[] args){
        /**
         * 线程控制1
         * */
//        TestThread testThread = new TestThread();
//        for(int i=0;i<5;i++){
//            Thread t = new Thread(testThread);
//            t.start();
//        }
        
        /**
         * 线程优先级设置
         * */
//        TestThread testThread = new TestThread();
//        Thread t_1 = new Thread(testThread);
//        Thread t_2 = new Thread(testThread);
//        Thread t_3 = new Thread(testThread);
//        Thread t_4 = new Thread(testThread);
//        Thread t_5 = new Thread(testThread);
//        t_5.setPriority(10);
//        t_5.start();t_1.start();t_2.start();t_3.start();t_4.start();
        
        
//        /**
//         * 线程停止运行的方法
//         * */
//        System.out.println(Thread.currentThread()+"开始运行");
//        TestThread test = new TestThread();
//        Thread t_1 = new Thread(test);
//        Thread t_2 = new Thread(test);
//        Thread t_3 = new Thread(test);
//        try{
//            t_1.start();t_2.start();t_3.start();
//            t_1.join();
//        }catch(Exception e){}
//        System.out.println(Thread.currentThread()+"停止运行");
        
        /**
         * 未同步线程
         * */
//        TestThreads testThreads = new TestThreads();
//        Thread t_1 = new Thread(testThreads);
//        Thread t_2 = new Thread(testThreads);
//        Thread t_3 = new Thread(testThreads);
//        t_1.start();t_2.start();t_3.start();
        
        
       /**
        * 同步的条件:引用同个target线程对象
        * */
//        SynThread ft = new SynThread();
//        LockTest lockTest = new LockTest();
//        LockThread lockThread = new LockThread(lockTest);
//        for(int i=0 ;i<4;i++){
////            Thread t = new Thread(ft);
////            t.start();
//            Thread s = new Thread(lockThread);
//            s.start();
//        }
        
        /**
         * 线程通讯
         * 要实现线程通讯(两个不同对象不同run方法的线程)
         * 1. 同步资源
         * 2. 当一个线程获取时,另外的线程停止。
         * */
        Requests request = new Requests();
        Productor p = new Productor(request);
        Consumer c = new Consumer(request);
    }
}
View Code

 

posted @ 2019-08-25 14:57  HJLのH  阅读(370)  评论(0编辑  收藏  举报