zoukankan      html  css  js  c++  java
  • Java多线程编程

    Java 多线程在面试里面也是经常问到的问题,看了很多总结和教程,接下来总结一下

    一、多线程的优点和缺点:

    多线程的优点:

    1.使用线程可以把占据时间长的程序中的任务放到后台去处理

    2.用户界面更加吸引人,这样比如用户点击了一个按钮去触发某件事件的处理,可以弹出一个进度条来显示处理的进度

    3.程序的运行效率可能会提高

    4.在一些等待的任务实现上如用户输入,文件读取和网络收发数据等,线程就比较有用了.

    多线程的缺点:

    1.如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换.

    2.更多的线程需要更多的内存空间

    3.线程中止需要考虑对程序运行的影响.

    4.通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生

    二、如何创建多线程:

    Java提供了三种创建线程的方法:

    1.通过实现Runnable接口

    2.通过继承Thread类的本身

    3.通过Callable和Future创建线程

    通过实现Runnable接口来创建线程:

    创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类。为了实现 Runnable,一个类只需要执行一个方法调用 run(),声明如下:

    public void run()

    你可以重写该方法,重要的是理解的 run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。在创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象。Thread 定义了几个构造方法,下面的这个是我们经常使用的:

    Thread(Runnable threadOb,String threadName);

    这里,threadOb 是一个实现 Runnable 接口的类的实例,并且 threadName 指定新线程的名字。新线程创建之后,你调用它的 start() 方法它才会运行。

    void start();

    下面是通过Runnable创建的一个线程并让他执行的实例:

    public class runnableThread implements Runnable {
    
        @Override
        public void run() {
            Thread thread = Thread.currentThread();
            for(int j=0;j<10;j++) {
                try {
                    thread.sleep(50);
                    System.out.println("当前线程是:"+thread.getName());
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        
        public static void main(String[] args) {
            runnableThread rThread = new runnableThread();
            Thread t1 = new Thread(rThread, "线程1");
            Thread t2 = new Thread(rThread, "线程2");
            t1.start();
            t2.start();
        }
    }
    /* 执行结果
    当前线程是:线程2
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程2
    当前线程是:线程1
    当前线程是:线程2
    当前线程是:线程2
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程2
    当前线程是:线程2
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程2
    当前线程是:线程1
    当前线程是:线程2
    当前线程是:线程2
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程2
    */

    通过Thread来创建线程:

    创建一个线程的第二种方法是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。

    public class myThread extends Thread {
        
        @Override
        public void run() {
             this.setName("线程1");
             System.out.println(this.getName()+ " 启动了");
             for(int j=0;j<10;j++) {
                 try {
                    this.sleep(50);
                    System.out.println("当前线程是:"+this.getName());
                } catch (Exception e) {
                    e.printStackTrace();
                }
             }
        }
        
        public static void main(String[] args) throws Exception {
            myThread thread = new myThread();
            Thread mainThread = Thread.currentThread();
            mainThread.setName("线程0");
            thread.start();
            System.out.println(mainThread.getName() + "启动了");
            for(int j=0;j<10;j++) {
                mainThread.sleep(50);
                System.out.println("当前线程是:"+mainThread.getName());
            }
        }
    }
    /*运行结果
    线程0启动了
    线程1 启动了
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    */

    通过 Callable 和 Future 创建线程:

    1. 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。

    2. 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。

    3. 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

    4. 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值

    代码如下:

    package thread;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    public class CallableThread implements Callable<Integer> {
    
        @Override
        public Integer call() throws Exception {
            Integer sum = 0;
            Thread cThread = Thread.currentThread();
            for(int j=1;j<=10;j++) {
                System.out.println("当前线程是:"+cThread.getName());
                sum+=j;
            }
            return sum;
        }
    
        public static void main(String[] args) throws Exception {
            CallableThread cThread = new  CallableThread();
            FutureTask<Integer> fTask = new FutureTask<Integer>(cThread);
            FutureTask<Integer> fTask1 = new FutureTask<Integer>(cThread);
            
            
            Thread thread = new Thread(fTask,"线程0");
            Thread t = new Thread(fTask1,"线程1");
            
            thread.start();
            t.start();
            System.out.println(fTask1.get());
            System.out.println(fTask.get());
    
        }
    
    }
    /*执行结果
     当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程0
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    55
    55
    */

    三、线程的生命周期

    觉得菜鸟教程的写的不错copy一下

    • 新建状态:

      使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

    • 就绪状态:

      当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

    • 运行状态:

      如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

    • 阻塞状态:

      如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

      • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

      • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

      • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

    • 死亡状态:

      一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态

    四、线程的管理

    Java提供了很多便捷的方法管理线程,我们只挑选几个讲一下

    1、线程休眠  sleep

    如果我们需要让当前正在执行的线程暂停一段时间并进入阻塞状态,可以通过Thread的sleep方法

    虽然时间不是很明显但我们能看出

    package thread;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    public class sleepThread extends Thread {
        
        @Override
        public void run() {
             this.setName("线程1");
             System.out.println(this.getName()+ " 启动了");
             for(int j=0;j<10;j++) {
                 try {
                    long start = System.currentTimeMillis();
                    System.out.println("开始休眠的时间"+start);
                    this.sleep(1000);
                    long end = System.currentTimeMillis();
                    System.out.println("结束休眠的时间"+end);
                    System.out.println("线程持续时间"+(end-start));
                } catch (Exception e) {
                    e.printStackTrace();
                }
             }
        }
        
        public static void main(String[] args) throws Exception {
            sleepThread thread = new sleepThread();
            Thread mainThread = Thread.currentThread();
            thread.start();
    
        }
    }
    /*
    线程1 启动了
    开始休眠的时间1550820958238
    结束休眠的时间1550820959238
    线程持续时间1000
    开始休眠的时间1550820959238
    结束休眠的时间1550820960239
    线程持续时间1001
    开始休眠的时间1550820960239
    结束休眠的时间1550820961239
    线程持续时间1000
    开始休眠的时间1550820961239
    结束休眠的时间1550820962239
    线程持续时间1000
    开始休眠的时间1550820962239
    结束休眠的时间1550820963240
    线程持续时间1001
    开始休眠的时间1550820963240
    结束休眠的时间1550820964240
    线程持续时间1000
    开始休眠的时间1550820964240
    结束休眠的时间1550820965241
    线程持续时间1001
    开始休眠的时间1550820965241
    结束休眠的时间1550820966242
    线程持续时间1001
    开始休眠的时间1550820966242
    结束休眠的时间1550820967243
    线程持续时间1001
    开始休眠的时间1550820967243
    结束休眠的时间1550820968243
    线程持续时间1000
    
    */

    2、线程合并 --join

    线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时,Thread类提供了join方法来完成这个功能,注意,它不是静态方法。从上面的方法的列表可以看到,它有3个重载的方法:

    void join()      
         当前线程等该加入该线程后面,等待该线程终止。    
    void join(long millis)  
         当前线程等待该线程终止的时间最长为 millis 毫秒。 如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度  
    void join(long millis,int nanos)   
         等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度  

    简单的实现:

    package thread;
    
    public class joinThread extends Thread {
        
        @Override
        public void run() {
            Thread thread = Thread.currentThread();
            for(int j=0;j<10;j++) {
                System.out.println("当前线程"+thread.getName());
            }
        }
        
        
        public static void main(String[] args) throws Exception {
            joinThread jThread1 = new joinThread();
            joinThread jThread2 = new joinThread();
            jThread1.start();
            jThread1.join();
            jThread2.start();
        }
    }
    /*
    当前线程Thread-0
    当前线程Thread-0
    当前线程Thread-0
    当前线程Thread-0
    当前线程Thread-0
    当前线程Thread-0
    当前线程Thread-0
    当前线程Thread-0
    当前线程Thread-0
    当前线程Thread-0
    当前线程Thread-1
    当前线程Thread-1
    当前线程Thread-1
    当前线程Thread-1
    当前线程Thread-1
    当前线程Thread-1
    当前线程Thread-1
    当前线程Thread-1
    当前线程Thread-1
    当前线程Thread-1
    */

    3、设置线程的优先级

         每个线程执行时都有一个优先级的属性,优先级高的线程可以获得较多的执行机会,而优先级低的线程则获得较少的执行机会。与线程休眠类似,线程的优先级仍然无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的也并非没机会执行。

    每个线程默认的优先级都与创建它的父线程具有相同的优先级,在默认情况下,main线程具有普通优先级。

    注:Thread类提供了setPriority(int newPriority)和getPriority()方法来设置和返回一个指定线程的优先级,其中setPriority方法的参数是一个整数,范围是1~·0之间,也可以使用Thread类提供的三个静态常量:

    MAX_PRIORITY   =10
    
    MIN_PRIORITY   =1
    
    NORM_PRIORITY   =5

    代码

    package thread;
    
    import java.util.Date;
    
    public class priorityThread extends Thread {
        
        @Override
        public void run() {
             this.setName("线程1");
             System.out.println(this.getName()+ " 启动了,当前的线程优先级是:"+this.getPriority());
             for(int j=0;j<10;j++) {
                 System.out.println("当前线程是:"+this.getName());
             }
        }
        
        public static void main(String[] args) throws Exception {
            priorityThread thread = new priorityThread();
            Thread mainThread = Thread.currentThread();
            mainThread.setName("线程0");
            mainThread.setPriority(MAX_PRIORITY);
            thread.setPriority(MIN_PRIORITY);
            thread.start();
            System.out.println(mainThread.getName() + "启动了,当前的线程优先级是:"+mainThread.getPriority());
            for(int j=0;j<10;j++) {
                System.out.println("当前线程是:"+mainThread.getName());
            }
        }
    }
    /*
    线程0启动了,当前的线程优先级是:10
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    当前线程是:线程0
    线程1 启动了,当前的线程优先级是:1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1
    当前线程是:线程1*/

    4、后台(守护)线程

         守护线程使用的情况较少,但并非无用,举例来说,JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。守护线程的用途为:

         • 守护线程通常用于执行一些后台作业,例如在你的应用程序运行时播放背景音乐,在文字编辑器里做自动语法检查、自动保存等功能。

         • Java的垃圾回收也是一个守护线程。守护线的好处就是你不需要关心它的结束问题。例如你在你的应用程序运行的时候希望播放背景音乐,如果将这个播放背景音乐的线程设定为非守护线程,那么在用户请求退出的时候,不仅要退出主线程,还要通知播放背景音乐的线程退出;如果设定为守护线程则不需要了。

    测试结果可知 一旦主线程结束 守护线程也会结束

    package thread;
    
    import java.util.Date;
    
    public class daemonThread extends Thread {
    
        @Override
        public void run() {
             this.setName("线程1");
             System.out.println(this.getName()+ " 启动了");
             for(int j=0;j<100;j++) {
                 System.out.println("当前正在运行的线程是:"+this.getName()+" "+j);
             }
        }
        
        public static void main(String[] args) {
            daemonThread dThread = new daemonThread();
            dThread.setDaemon(true);
            
            Thread mainThread = Thread.currentThread();    
            mainThread.setName("线程0");
            dThread.start();
            for(int j=0;j<3;j++) {
                System.out.println("当前正在运行的线程是:"+mainThread.getName()+" "+j);
            }
    
        }
    
    }
    /**
     * 
     当前正在运行的线程是:线程0 0
    线程1 启动了
    当前正在运行的线程是:线程1 0
    当前正在运行的线程是:线程0 1
    当前正在运行的线程是:线程1 1
    当前正在运行的线程是:线程0 2
    当前正在运行的线程是:线程1 2
    当前正在运行的线程是:线程1 3
    当前正在运行的线程是:线程1 4
    当前正在运行的线程是:线程1 5
    当前正在运行的线程是:线程1 6
    当前正在运行的线程是:线程1 7
    当前正在运行的线程是:线程1 8
    当前正在运行的线程是:线程1 9
    当前正在运行的线程是:线程1 10
    当前正在运行的线程是:线程1 11
    当前正在运行的线程是:线程1 12
    当前正在运行的线程是:线程1 13
    当前正在运行的线程是:线程1 14
    当前正在运行的线程是:线程1 15
    当前正在运行的线程是:线程1 16
    当前正在运行的线程是:线程1 17
    当前正在运行的线程是:线程1 18
    当前正在运行的线程是:线程1 19
    当前正在运行的线程是:线程1 20
    当前正在运行的线程是:线程1 21
    当前正在运行的线程是:线程1 22
    当前正在运行的线程是:线程1 23
    当前正在运行的线程是:线程1 24
    当前正在运行的线程是:线程1 25
    */

    5、正确结束线程

    Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit这些终止线程运行的方法已经被废弃了,使用它们是极端不安全的!想要安全有效的结束一个线程,可以使用下面的方法:

        • 正常执行完run方法,然后结束掉;

        • 控制循环条件和判断条件的标识符来结束掉线程。

     • 采用 interrupt() 来中结束线程

    package thread;
    
    import java.util.Date;
    
    public class breakThread extends Thread {
    
        @Override
        public void run() {
             this.setName("线程1");
             System.out.println(this.getName()+ " 启动了");
             for(int j=0;j<10;j++) {
                 if(j==5) this.interrupt();//终止线程
                 if(this.isInterrupted()) { //判断是否被终止
                     break;
                 }
                 System.out.println("当前正在运行的线程是:"+this.getName()+" "+j);
             }
        }
        
        public static void main(String[] args) {
            breakThread dThread = new breakThread();
            dThread.start();
    
        }
    
    }
    /*
    线程1 启动了
    当前正在运行的线程是:线程1 0
    当前正在运行的线程是:线程1 1
    当前正在运行的线程是:线程1 2
    当前正在运行的线程是:线程1 3
    当前正在运行的线程是:线程1 4
    */

    五、多线程同步问题

    多线程同步问题我们先引入一个例子

    package thread;
    
    public class lockTicket implements Runnable {
        
        private int count = 10;
        
        @Override
        public void run() {
            Thread t = Thread.currentThread();
            while(count>0) {
                System.out.println("剩余个数为" + count + "   当前线程为:"+t.getName());
                count--;
            }
    
        }
    
        public static void main(String[] args) {
            lockTicket ticket = new lockTicket();
            Thread t1 = new Thread(ticket,"线程1");
            Thread t2 = new Thread(ticket,"线程2");
            Thread t3 = new Thread(ticket,"线程3");
            Thread t4 = new Thread(ticket,"线程4");
            t1.start();t2.start();t3.start();t4.start();
    
        }
    
    }
    /*
    输出:
    剩余个数为10 当前线程为:线程2 剩余个数为10 当前线程为:线程1 剩余个数为8 当前线程为:线程1 剩余个数为9 当前线程为:线程3 剩余个数为9 当前线程为:线程2 剩余个数为6 当前线程为:线程3 剩余个数为7 当前线程为:线程1 剩余个数为4 当前线程为:线程3 剩余个数为2 当前线程为:线程3 剩余个数为1 当前线程为:线程3 剩余个数为5 当前线程为:线程2 剩余个数为3 当前线程为:线程1 */

    我们观察输出结果发现 有些数字重复出现,出现的原因就是 CPU对线程执行的随机调度,比如A线程此时正在打印信息(红色代码段)还没打印完毕此时CPU切换到B线程执行了,B线程执行完了又切换回A线程执行就会导致上面现象发生。

    针对线程同步问题java早就有解决方法了,最简单的就是给方法加上synchronized关键字,如下:

    package thread;
    
    public class lockTicket implements Runnable {
        
        private int count = 10;
        private Object lock = new Object();
        @Override
        public void run() {
            Thread t = Thread.currentThread();
            while(count>0) {
                synchronized (lock) {
                    if(count>0) {
                        System.out.println("剩余个数为" + count + "   当前线程为:"+t.getName());
                        count--;
                    }else {
                        break;
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            lockTicket ticket = new lockTicket();
            Thread t1 = new Thread(ticket,"线程1");
            Thread t2 = new Thread(ticket,"线程2");
            Thread t3 = new Thread(ticket,"线程3");
            Thread t4 = new Thread(ticket,"线程4");
            t1.start();t2.start();t3.start();t4.start();
    
        }
    
    }
    /*剩余个数为10   当前线程为:线程1
    剩余个数为9   当前线程为:线程1
    剩余个数为8   当前线程为:线程1
    剩余个数为7   当前线程为:线程1
    剩余个数为6   当前线程为:线程3
    剩余个数为5   当前线程为:线程3
    剩余个数为4   当前线程为:线程3
    剩余个数为3   当前线程为:线程3
    剩余个数为2   当前线程为:线程3
    剩余个数为1   当前线程为:线程3
    */

    此时发现结果正常 ,加上synchronized关键字后,比如A线程执行输出方法就相当于拿到了一把锁,只有获取这个锁才能执行此方法,如果在A线程执行输出方法过程中B线程也想插一脚进来执行输出方法,对不起此时这是不能够的,因为此时锁在A线程手里,B线程无权拿到这把锁,只有等到A线程执行完后放弃锁,B线程才能拿到锁执行输出方法。

    那么通过继承Thread 怎么实现线程同步呢?

    package thread;
    
    public class lockSynchronizationThread extends Thread{
        
        
        public lockSynchronizationThread(String name) {
            super(name);
        }
        
        private static int count = 10;  //设置静态变量 实现共享!
        private static Object lock = new Object();// 锁共享
        @Override
        public void run() {
            Thread t = Thread.currentThread();
            while(count>0) {
                synchronized (lock) {
                    if(count>0) {
                        try {
                            t.sleep(50);
                            System.out.println("剩余个数为" + count + "   当前线程为:"+t.getName());
                            count--;
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else {
                        break;
                    }
                }
            }
        }
        
        
        public static void main(String[] args) {
            lockSynchronizationThread thread1 = new lockSynchronizationThread("线程1");
            lockSynchronizationThread thread2 = new lockSynchronizationThread("线程2");
            lockSynchronizationThread thread3 = new lockSynchronizationThread("线程3");
            lockSynchronizationThread thread4 = new lockSynchronizationThread("线程4");
            thread1.start();
            thread2.start();
            thread3.start();
            thread4.start();
    
        }
    
    }
    /*
    剩余个数为10   当前线程为:线程1
    剩余个数为9   当前线程为:线程1
    剩余个数为8   当前线程为:线程2
    剩余个数为7   当前线程为:线程4
    剩余个数为6   当前线程为:线程4
    剩余个数为5   当前线程为:线程3
    剩余个数为4   当前线程为:线程3
    剩余个数为3   当前线程为:线程4
    剩余个数为2   当前线程为:线程4
    剩余个数为1   当前线程为:线程2
    */

    除了synchronized关键字之外 Java 中java.util.concurrent.locks 提供了很多的锁 

    比如:

    java.util.concurrent.locks.Lock;
    java.util.concurrent.locks.ReentrantLock;
    java.util.concurrent.locks.ReadWriteLock;
    java.util.concurrent.locks.ReentrantReadWriteLock;

    下面介绍一下 ReentrantLock 的使用

    package thread;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class lockReentreatLockThread implements Runnable {
        
        private int count = 10;
        private ReentrantLock lock = new ReentrantLock();
        @Override
        public void run() {
            Thread t = Thread.currentThread();
            while(count>0) {
                lock.lock(); //加锁
                try {
                    if(count>0) {
                        System.out.println("剩余个数为" + count + "   当前线程为:"+t.getName());
                        count--;
                    }
                }finally {
                    lock.unlock();//解锁
                }
                // 使用try catch 防止出现异常无法解锁的问题
                
            }
        }
    
        public static void main(String[] args) {
            lockReentreatLockThread ticket = new lockReentreatLockThread();
            Thread t1 = new Thread(ticket,"线程1");
            Thread t2 = new Thread(ticket,"线程2");
            Thread t3 = new Thread(ticket,"线程3");
            Thread t4 = new Thread(ticket,"线程4");
            t1.start();t2.start();t3.start();t4.start();
    
        }
    
    }
    /*剩余个数为10   当前线程为:线程2
    剩余个数为9   当前线程为:线程1
    剩余个数为8   当前线程为:线程1
    剩余个数为7   当前线程为:线程3
    剩余个数为6   当前线程为:线程3
    剩余个数为5   当前线程为:线程3
    剩余个数为4   当前线程为:线程3
    剩余个数为3   当前线程为:线程3
    剩余个数为2   当前线程为:线程3
    剩余个数为1   当前线程为:线程4
    */

    六、线程池

    看了很多 介绍觉得这篇写的挺好的 我这里就不再copy了

    贴上链接  点击这里了解线程池

    参考文章:

    http://www.runoob.com/java/java-multithreading.html

    https://www.cnblogs.com/luxiaoxun/p/3870265.html

    https://www.cnblogs.com/snow-flower/p/6114765.html

    https://www.cnblogs.com/hankzhouAndroid/p/8693278.html

    https://www.cnblogs.com/aspirant/p/6920418.html

  • 相关阅读:
    开篇之作
    瀑布流特效
    随写
    关于冒泡排序的补充
    New start-开始我的学习记录吧
    java中序列化的简单认识
    我的Python之路
    算法学习笔记
    Leaflet个人封装笔记
    反射获取config实体类属性并赋值
  • 原文地址:https://www.cnblogs.com/Dvelpro/p/10420027.html
Copyright © 2011-2022 走看看