Java高级学习之路之篇七(java多线程)

Java 多线程编程

Java 给多线程编程提供了内置的支持。 一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。

这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。

多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。

一个线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

下图显示了一个线程完整的生命周期。

  • 新建状态:

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

  • 就绪状态:

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

  • 运行状态:

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

  • 阻塞状态:

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

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

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

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

  • 死亡状态:

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

线程的优先级

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。

具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。


>>>>>> 如何创建一个线程 <<<<<<

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

  • 通过实现 Runnable 接口;
  • 通过继承 Thread 类本身;
  • 通过 Callable 和 Future 创建线程。

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

创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类。

为了实现 Runnable,一个类只需要执行一个方法调用 run(),声明如下:

public void run()

你可以重写该方法,重要的是理解的 run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。

在创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象。

Thread 定义了几个构造方法,下面的这个是我们经常使用的:

Thread(Runnable threadOb,String threadName);

这里,threadOb 是一个实现 Runnable 接口的类的实例,并且 threadName 指定新线程的名字。

新线程创建之后,你调用它的 start() 方法它才会运行。

void start();

下面是一个创建线程并开始让它执行的实例:

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;
   
   RunnableDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // 让线程睡眠一会
            Thread.sleep(50);
         }
      }catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}
 
public class TestThread {
 
   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo( "Thread-1");
      R1.start();
      
      RunnableDemo R2 = new RunnableDemo( "Thread-2");
      R2.start();
   }   
}

编译以上程序运行结果如下:

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

2、通过继承Thread来创建线程

创建一个线程的第二种方法是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。

继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。

该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // 让线程睡眠一会
            Thread.sleep(50);
         }
      }catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}
 
public class TestThread {
 
   public static void main(String args[]) {
      ThreadDemo T1 = new ThreadDemo( "Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo( "Thread-2");
      T2.start();
   }   
}

编译以上程序运行结果如下:

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Thread 方法

下表列出了Thread类的一些重要方法:

序号 方法描述
1 public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
2 public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
3 public final void setName(String name)
改变线程名称,使之与参数 name 相同。
4 public final void setPriority(int priority)
 更改线程的优先级。
5 public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
6 public final void join(long millisec)
等待该线程终止的时间最长为 millis 毫秒。
7 public void interrupt()
中断线程。
8 public final boolean isAlive()
测试线程是否处于活动状态。

测试线程是否处于活动状态。 上述方法是被Thread对象调用的。下面的方法是Thread类的静态方法。

序号 方法描述
1 public static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
2 public static void sleep(long millisec)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
3 public static boolean holdsLock(Object x)
当且仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
4 public static Thread currentThread()
返回对当前正在执行的线程对象的引用。
5 public static void dumpStack()
将当前线程的堆栈跟踪打印至标准错误流。

实例

如下的ThreadClassDemo 程序演示了Thread类的一些方法:

// 文件名 : DisplayMessage.java
// 通过实现 Runnable 接口创建线程
public class DisplayMessage implements Runnable {
   private String message;
   
   public DisplayMessage(String message) {
      this.message = message;
   }
   
   public void run() {
      while(true) {
         System.out.println(message);
      }
   }
}
// 文件名 : GuessANumber.java
// 通过继承 Thread 类创建线程
 
public class GuessANumber extends Thread {
   private int number;
   public GuessANumber(int number) {
      this.number = number;
   }
   
   public void run() {
      int counter = 0;
      int guess = 0;
      do {
         guess = (int) (Math.random() * 100 + 1);
         System.out.println(this.getName() + " guesses " + guess);
         counter++;
      } while(guess != number);
      System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
   }
}
// 文件名 : ThreadClassDemo.java
public class ThreadClassDemo {
 
   public static void main(String [] args) {
      Runnable hello = new DisplayMessage("Hello");
      Thread thread1 = new Thread(hello);
      thread1.setDaemon(true);
      thread1.setName("hello");
      System.out.println("Starting hello thread...");
      thread1.start();
      
      Runnable bye = new DisplayMessage("Goodbye");
      Thread thread2 = new Thread(bye);
      thread2.setPriority(Thread.MIN_PRIORITY);
      thread2.setDaemon(true);
      System.out.println("Starting goodbye thread...");
      thread2.start();
 
      System.out.println("Starting thread3...");
      Thread thread3 = new GuessANumber(27);
      thread3.start();
      try {
         thread3.join();
      }catch(InterruptedException e) {
         System.out.println("Thread interrupted.");
      }
      System.out.println("Starting thread4...");
      Thread thread4 = new GuessANumber(75);
      
      thread4.start();
      System.out.println("main() is ending...");
   }
}

运行结果如下,每一次运行的结果都不一样。

3、通过 Callable 和 Future 创建线程

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

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

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

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

实例

public class CallableThreadTest implements Callable<Integer> {
    public static void main(String[] args)  
    {  
        CallableThreadTest ctt = new CallableThreadTest();  
        FutureTask<Integer> ft = new FutureTask<>(ctt);  
        for(int i = 0;i < 100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);  
            if(i==20)  
            {  
                new Thread(ft,"有返回值的线程").start();  
            }  
        }  
        try  
        {  
            System.out.println("子线程的返回值:"+ft.get());  
        } catch (InterruptedException e)  
        {  
            e.printStackTrace();  
        } catch (ExecutionException e)  
        {  
            e.printStackTrace();  
        }  
  
    }
    @Override  
    public Integer call() throws Exception  
    {  
        int i = 0;  
        for(;i<100;i++)  
        {  
            System.out.println(Thread.currentThread().getName()+" "+i);  
        }  
        return i;  
    }  
}

创建线程的三种方式的对比

  • 1. 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。

  • 2. 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。


>>>>>> JAVA多线程提升篇 <<<<<<

  • 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程
  • 并行与并发
    • 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
    • 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。

并发与并行

  • 线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,cpu是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码。
  • 同步:Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入 @synchronized 关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。

1、线程的调度与控制

线程的调度模型分为: 分时调度模型和抢占式调度模型,Java使用抢占式调度模型 
通常我们的计算机只有一个 CPU,CPU 在某一个时刻只能执行一条指令,线程只有得到 CPU时间片,也就是使用权,才可以执行指令。在单 CPU 的机器上线程不是并行运行的,只有在多个 CPU 上线程才可以并行运行。Java 虚拟机要负责线程的调度,取得 CPU 的使用权,目前有两种调度模型:分时调度模型和抢占式调度模型,Java 使用抢占式调度模型。分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。分时调度模型: 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
抢占式调度模型: 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些.

public class ThreadTest {

    public static void main(String[] args) {
        ThreadTest1();
        ThreadTest2();
        ThreadTest3();
        ThreadTest4();
        ThreadTest5();
    }

    /**
     * 三个方法: 获取当前线程对象:Thread.currentThread(); 给线程起名: t1.setName("t1"); 获取线程的名字: t.getName();
     */
    private static void ThreadTest1() {
        Thread t = Thread.currentThread();// t保存的内存地址指向的线程为"主线程"
        System.out.println(t.getId());
        Thread t1 = new Thread(new Processor1());
        // 给线程起名
        t1.setName("t1");
        t1.start();
        Thread t2 = new Thread(new Processor1());
        t2.setName("t2");
        t2.start();
    }

    /**
     * 线程优先级高的获取的CPU时间片相对多一些 优先级: 1-10 最低: 1 最高: 10 默认: 5
     */
    private static void ThreadTest2() {
        Thread t1 = new Processor2();
        Thread t2 = new Processor2();
        t1.setName("t1");
        t2.setName("t2");

        System.out.println(t1.getPriority());
        System.out.println(t2.getPriority());
        t1.setPriority(1);
        t2.setPriority(10);

        t1.start();
        t2.start();
    }

    /**
     * 1.Thread.sleep(毫秒); 2.sleep方法是一个静态方法 3.该方法的作用: 阻塞当前线程,腾出CPU,让给其它线程
     */
    private static void ThreadTest3() {
        Thread t = new Thread(new Processor3());
        t.start();
        for (int i = 0; i < 11; i++) {
            System.out.println(Thread.currentThread().getName() + "========>" + i);
            try {
                t.sleep(5000);// 等同于Thread.sleep(5000);阻塞的还是当前线程,和t线程无关.
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 某线程正在休眠,如何打断它的休眠 以下方式依靠的是异常处理机制
     */
    private static void ThreadTest4() {
        try {
            Thread t = new Thread(new Processor4());
            t.start();
            Thread.sleep(5000);// 睡5s
            t.interrupt();// 打断Thread的睡眠
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 如何正确的更好的终止一个正在执行的线程 需求:线程启动5s之后终止.
     */
    private static void ThreadTest5() {
        Processor5 p = new Processor5();
        Thread t = new Thread(p);
        t.start();
        // 5s之后终止
        try {
            Thread.sleep(5000);
            p.isRun = false;
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class Processor1 implements Runnable {

    @Override
    public void run() {
        Thread t = Thread.currentThread();// t保存的内存地址指向的线程为"t1线程对象"
        System.out.println(t.getName());
        System.out.println(t.getId());

    }
}

class Processor2 extends Thread {

    @Override
    public void run() {
        for (int i = 0; i < 50; i++) {
            System.out.println(Thread.currentThread().getName() + "----------->" + i);
        }

    }
}

class Processor3 implements Runnable {

    /**
     * Thread中的run方法不能抛出异常,所以重写runn方法之后,在run方法的声明位置上不能使用throws 所以run方法中的异常只能try...catch...
     */
    @Override
    public void run() {
        for (int i = 0; i < 11; i++) {
            System.out.println(Thread.currentThread().getName() + "========>" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

class Processor4 implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(1000000000);
            System.out.println("能否执行这里");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 11; i++) {
            System.out.println(Thread.currentThread().getName() + "========>" + i);
        }
    }
}

class Processor5 implements Runnable {

    boolean isRun = true;

    @Override
    public void run() {
        for (int i = 0; i < 11; i++) {
            if (isRun) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "========>" + i);
            }
        }
    }
}

2、线程优先级

线 程 优 先 级 主 要 分 三 种 : MAX_PRIORITY( 最 高 级 );MIN_PRIORITY ( 最 低 级 )NORM_PRIORITY(标准)默认

//设置线程的优先级,线程启动后不能再次设置优先级
//必须在启动前设置优先级
//设置最高优先级
t1.setPriority(Thread.MAX_PRIORITY);

3、sleep

sleep 设置休眠的时间,单位毫秒,当一个线程遇到 sleep 的时候,就会睡眠,进入到阻塞状态,放弃 CPU,腾出 cpu 时间片,给其他线程用,所以在开发中通常我们会这样做,使其他的线程能够取得 CPU 时间片,当睡眠时间到达了,线程会进入可运行状态,得到 CPU 时间片继续执行,如果线程在睡眠状态被中断了,将会抛出 IterruptedException

   public class ThreadTest05 {
        public static void main(String[] args) {
            Runnable r1 = new Processor();
            Thread t1 = new Thread(r1, "t1");
            t1.start();
            Thread t2 = new Thread(r1, "t2");
            t2.start();
        }
    }
    class Processor implements Runnable {
        public void run() {
            for (int i=0; i<100; i++) {
            System.out.println(Thread.currentThread().getName() + "," + i);
        if (i % 10 == 0) {
            try {
        //睡眠 100 毫秒,主要是放弃 CPU 的使用,将 CPU 时间片交给其他线程使用
        Thread.sleep(100);
            }catch(InterruptedException e) {
            e.printStackTrace();
                }
            }
        }
    }
}

3、停止一个线程

如果我们的线程正在睡眠,可以采用 interrupt 进行中断
通常定义一个标记,来判断标记的状态停止线程的执行

4、yield

它与 sleep()类似,只是不能由用户指定暂停多长时间,并且 yield()方法只能让同优先级的线程有执行的机会,采用 yieid 可以将 CPU 的使用权让给同一个优先级的线程

5、join

当前线程可以调用另一个线程的 join 方法,调用后当前线程会被阻塞不再执行,直到被调用的线程执行完毕,当前线程才会执行

6、synchronized

线程同步,指某一个时刻,指允许一个线程来访问共享资源,线程同步其实是对对象加锁,如果对象中的方法都是同步方法,那么某一时刻只能执行一个方法,采用线程同步解决以上的问题,我们只要保证线程一操作 s 时,线程 2 不允许操作即可,只有线程一使用完成 s 后,再让线程二来使用 s 变量

异步编程模型 : t1线程执行t1的,t2线程执行t2的,两个线程之间谁也不等谁.
同步编程模型 : t1线程和t2线程执行,t2线程必须等t1线程执行结束之后,t2线程才能执行,这是同步编程模型.

什么时候要用同步呢?为什么要引入线程同步呢?
1.为了数据的安全,尽管应用程序的使用率降低,但是为了保证数据是安全的,必须加入线程同步机制.
线程同步机制使程序变成了(等同)单线程.
2.什么条件下要使用线程同步?
第一: 必须是多线程环境
第二: 多线程环境共享同一个数据.
第三: 共享的数据涉及到修改操作.

//synchronized 是对对象加锁
//采用 synchronized 同步最好只同步有线程安全的代码
//可以优先考虑使用 synchronized 同步块
//因为同步的代码越多,执行的时间就会越长,其他线程等待的时间就会越长
//影响效率
    public synchronized void run() {
//使用同步块
    synchronized (this) {
        for (int i=0; i<10; i++) {
            s+=i;
        }
    System.out.println(Thread.currentThread().getName() + ", s=" + s);
    s = 0;
    }
public class SynchronizedTest {
    public static void main(String[] args) {
        SynchronizeTest1();
    }

    private static void SynchronizeTest1() {
        Account account=new Account("Actno-001",5000.0);
        Thread t1=new Thread(new Processor(account));
        Thread t2=new Thread(new Processor(account));
        t1.start();
        t2.start();
    }

}
/**
 * 取款线程
 */
class Processor implements Runnable{
    Account act;
    Processor(Account act){
        this.act=act;
    }
    @Override
    public void run() {
        act.withdraw(1000.0);
        System.out.println("取款1000.0成功,余额: "+act.getBalance());
    }

}
class Account {

    private String actno;
    private double balance;

    public Account() {
        super();
    }

    public Account(String actno, double balance) {
        super();
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
        return actno;
    }

    public void setActno(String actno) {
        this.actno = actno;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    /**
     * 对外提供一个取款的方法 对当前账户进行取款操作
     */
    public void withdraw(double money) {
        //把需要同步的代码,放到同步语句块中.
        //遇到synchronized就找锁,找到就执行,找不到就等
        /**
         * 原理: t1线程和t2线程
         * t1线程执行到此处,遇到了synchronized关键字,就会去找this的对象锁,
         * 如果找到this对象锁,则进入同步语句块中执行程序,当同步语句块中的代码执行结束之后,
         * t1线程归还this的对象锁.
         * 
         * 在t1线程执行同步语句块的过程中,如果t2线程也过来执行以下代码,也遇到synchronized关键字,
         * 所以也去找this对象锁,但是该对象锁被t1线程持有,只能在这等待this对象的归还.
         * 
         * synchronized关键字添加到成员方法上,线程拿走的也是this的对象锁.
         * 
         */
        synchronized (this) {
            double after = balance - money;
            try {
                //延迟
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //更新
            this.setBalance(after);
        }
    }
}
public class SynchronizedTest2 {
    public static void main(String[] args) throws InterruptedException {
        MyClass mc1=new MyClass();
        MyClass mc2=new MyClass();
        Thread t1=new Thread(new Runnable1(mc1));
        Thread t2=new Thread(new Runnable1(mc2));
        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        //延迟,保证t1先执行
        Thread.sleep(1000);
        t2.start();
    }
}
class Runnable1 implements Runnable{
    MyClass mc;
    Runnable1(MyClass mc){
        this.mc=mc;
    }
    @Override
    public void run() {
        if("t1".equals(Thread.currentThread().getName())){
            MyClass.m1();//因为是静态方法,用的还是类锁,和对象锁无关
        }
        if("t2".equals(Thread.currentThread().getName())){
            MyClass.m2();
        }
    }
}
class MyClass{
    //synchronized添加到静态方法上,线程执行此方法的时候会找类锁,类锁只有一把
    public synchronized static void m1(){
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("m1()............");
    }
    /**
     *  m2()不会等m1结束,因为该方法没有被synchronized修饰
     */
//    public static void m2(){
//        System.out.println("m2()........");
//    }
    /**
     * m2方法等m1结束之后才能执行,该方法有synchronized
     * 线程执行该方法需要"类锁",而类锁只有一个.
     */
    public synchronized static void m2(){
        System.out.println("m2()........");
    }
}

7、死锁

public class DeadLock {

    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();
        Thread t1 = new Thread(new T1(o1, o2));
        Thread t2 = new Thread(new T2(o1, o2));
        t1.start();
        t2.start();
    }
}

class T1 implements Runnable {

    Object o1;
    Object o2;

    T1(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }

    @Override
    public void run() {
        synchronized (o1) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o2) {

            }
        }
    }
}

class T2 implements Runnable {

    Object o1;
    Object o2;

    T2(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }

    @Override
    public void run() {
        synchronized (o2) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o1) {

            }
        }
    }
}

8、守护线程

从线程分类上可以分为:用户线程(以上讲的都是用户线程),另一个是守护线程。守护线程是这样的,所有的用户线程结束生命周期,守护线程才会结束生命周期,只要有一个用户线程存在,那么守护线程就不会结束,例如 java 中著名的垃圾回收器就是一个守护线程,只有应用程序中所有的线程结束,它才会结束。其它所有的用户线程结束,则守护线程退出!
守护线程一般都是无限执行的.

public class DaemonThread {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new Runnable2());
        t1.setName("t1");
        // 将t1这个用户线程修改成守护线程.在线程没有启动时可以修改以下参数
        t1.setDaemon(true);
        t1.start();
        // 主线程
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "----->" + i);
            Thread.sleep(1000);
        }
    }
}

class Runnable2 implements Runnable {

    @Override
    public void run() {
        int i = 0;
        while (true) {
            i++;
            System.out.println(Thread.currentThread().getName() + "-------->" + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

设置为守护线程后,当主线程结束后,守护线程并没有把所有的数据输出完就结束了,也即是说守护线程是为用户线程服务的,当用户线程全部结束,守护线程会自动结束

9、Timer.schedule()

/**
 * 关于定时器的应用 作用: 每隔一段固定的时间执行一段代码
 */
public class TimerTest {

    public static void main(String[] args) throws ParseException {
        // 1.创建定时器
        Timer t = new Timer();
        // 2.指定定时任务
        t.schedule(new LogTimerTask(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse("2017-06-29 14:24:00 000"), 10 * 1000);
    }
}

// 指定任务
class LogTimerTask extends TimerTask {

    @Override
    public void run() {
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()));
    }
}

--------------------- 
原文:https://blog.csdn.net/fang323619/article/details/73904351 

猜你喜欢

转载自blog.csdn.net/weixin_40873462/article/details/90069217