java线程基础知识整理 线程基本概念 1、java实现线程 2、线程的生命周期 3、线程常用的方法   4、线程调度 4.5、线程安全   5、定时任务 6、通过Callable接口实现一个线程 7、Object类中的wait和notify方法

目录

线程基本概念

1、java实现线程

2、线程的生命周期

3、线程常用的方法

3.1、sleep()

3.2、interrupt方法

3.3、stop方法

 

4、线程调度

4.1、常见的线程调度模型

4.2、java中提供的线程调度方法

4.3、线程让步

4.4、线程合并

4.5、线程安全

4.5.1、线程同步的实现

4.5.2、java中的线程安全性

4.5.3、synchronized总结

4.5.4、死锁

4.6、守护线程

4.6.1、守护线程的特点

4.6.2、一个简单的守护线程的例子

5、定时任务

5.1、实现一个定时器

6、通过Callable接口实现一个线程

7、Object类中的wait和notify方法

7.1、wait和notify方法介绍

7.2、生产者和消费者模式

7.3、实现奇偶数的交替输出


1、什么是进程?什么是线程?

进程是一个应用程序,线程是一个进程中的执行场景/执行单元。一个进程可以启动多个线程。在java语言中对于两个线程A和B,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈。 在使用了多线程机制之后,main()方法结束了,只是主线程结束了,主栈空了,但其他线程不一定结束,其他栈(线程)可能还在压栈弹栈。

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

1、java实现线程

java语言支持多线程机制。并且java已经实现了多线程(java.lang.Thread类和java.lang.Runnable接口)

第一种实现方式(继承java.lang.Thread类并重写run方法)

  1.  
    public class Thread_01 extends Thread {
  2.  
    @Override
  3.  
    public void run() {
  4.  
    super.run();
  5.  
    System.out.println("第一个线程");
  6.  
    }
  7.  
     
  8.  
    public static void main(String[] args) {
  9.  
    Thread thread=new Thread(new Thread_01(),"first_thread"); //创建线程对象
  10.  
    thread.start(); //启动线程 start方法使线程处于就绪队列,等待CPU调用
  11.  
    }
  12.  
    }

第二种实现方式(实现java.lang.Runnable接口并实现run方法)

  1.  
    public class Thread_02 implements Runnable {
  2.  
    @Override
  3.  
    public void run() {
  4.  
    System.out.println("第一个线程");
  5.  
    }
  6.  
    public static void main(String[] args) {
  7.  
    Thread thread=new Thread(new Thread_01(),"first_thread"); //创建线程对象
  8.  
    thread.start(); //启动线程 start方法使线程处于就绪队列,等待CPU调用
  9.  
    }
  10.  
    }

通常使用第二种方法,因为一个类实现了接口还可以继承其他类

注意:start方法和run方法的区别!!!

run方法不会启动线程。

start方法的作用是:启动一个线程,在JVM中为线程开辟一个新的栈空间。之后start方法就结束了。线程启动成功并进入排队等待序列。等到被CPU调用到,就会自动调用run方法。

2、线程的生命周期

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

3、线程常用的方法

3.1、sleep()

public static native void sleep(long millis) throws InterruptedException;

作用:让线程进入休眠,进入“阻塞状态”。放弃占有CPU时间片,让其他线程使用。

  1.  
    public class Thread_03 {
  2.  
    public static void main(String[] args) throws InterruptedException {
  3.  
    Thread thread=new Thread(new Thread_03_1(),"Thread_03_1");
  4.  
    thread.start();
  5.  
    int count=1; //计数器
  6.  
    while(true){
  7.  
    System.out.println("Thread_03_1线程沉睡了"+count+++"秒");
  8.  
    Thread.sleep(1000);
  9.  
    if(count>5){
  10.  
    break;
  11.  
    }
  12.  
    }
  13.  
    }
  14.  
     
  15.  
    }
  16.  
     
  17.  
    class Thread_03_1 implements Runnable{
  18.  
    @Override
  19.  
    public void run() {
  20.  
    try {
  21.  
    Thread.sleep(1000*5); //让线程沉睡(等待) 5秒
  22.  
    } catch (InterruptedException e) {
  23.  
    e.printStackTrace();
  24.  
    }
  25.  
    System.out.println(Thread.currentThread().getName());
  26.  
    }
  27.  
    }

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

3.2、interrupt方法

interrupt方法可以中断线程的睡眠,依靠了java异常处理机制

  1.  
    public class Thread_03 {
  2.  
    public static void main(String[] args) throws InterruptedException {
  3.  
    Thread thread=new Thread(new Thread_03_1(),"Thread_03_1");
  4.  
    thread.start();
  5.  
    int count=1; //计数器
  6.  
    while(true){
  7.  
    System.out.println("Thread_03_1线程沉睡了"+count+++"秒");
  8.  
    Thread.sleep(1000);
  9.  
    if(count==3){
  10.  
    System.out.println("打断Thread_03_1睡眠");
  11.  
    thread.interrupt();
  12.  
    break;
  13.  
    }
  14.  
    }
  15.  
    }
  16.  
     
  17.  
    }
  18.  
     
  19.  
    class Thread_03_1 implements Runnable{
  20.  
    @Override
  21.  
    public void run() {
  22.  
    try {
  23.  
    Thread.sleep(1000*5); //让线程沉睡(等待) 5秒
  24.  
    } catch (InterruptedException e) {
  25.  
    e.printStackTrace();
  26.  
    }
  27.  
    System.out.println(Thread.currentThread().getName()+"醒了");
  28.  
    }
  29.  
    }

线程Thread_03_1原计划沉睡5秒,在它睡到3秒时,使用interrupt方法打断其睡眠。

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

3.3、stop方法

stop方法可以强制终止一个线程的执行。不过这种方式容易丢失数据。因为这种方式会直接杀死线程,线程没有保存的数据会丢失。所以不建议使用

  1.  
    public class Thread_04 {
  2.  
    public static void main(String[] args) throws InterruptedException {
  3.  
    Thread thread = new Thread(new Thread_04_1(), "Thread_03_1");
  4.  
    thread.start();
  5.  
    //线程Thread_03_1执行5秒后,强制结束线程Thread_03_1
  6.  
    Thread.sleep(1000*6);
  7.  
    System.out.println("强制终止线程Thread_03_1");
  8.  
    thread.stop(); //强制终止线程
  9.  
    }
  10.  
    }
  11.  
    class Thread_04_1 implements Runnable{
  12.  
    @Override
  13.  
    public void run() {
  14.  
    for(int i=1;i<1000;i++) {
  15.  
    try {
  16.  
    Thread.sleep(1000);
  17.  
    } catch (InterruptedException e) {
  18.  
    e.printStackTrace();
  19.  
    }
  20.  
    System.out.println(Thread.currentThread()+"-->"+i+"秒");
  21.  
    }
  22.  
    }
  23.  
    }

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

建议使用如下方法结束一个线程:在线程类中增加一个布尔类型的变量run,通过改变run的值,来控制线程运行/停止状态。

  1.  
    public class Thread_05 {
  2.  
    public static void main(String[] args) throws InterruptedException {
  3.  
    Thread_05_1 t=new Thread_05_1();
  4.  
    Thread thread = new Thread(t, "Thread_05_1");
  5.  
    thread.start();
  6.  
    //等候5秒之后,终止该线程
  7.  
    Thread.sleep(1000*5);
  8.  
    t.run=false;
  9.  
    System.out.println(thread.getName()+"线程已暂停");
  10.  
    }
  11.  
    }
  12.  
     
  13.  
    class Thread_05_1 implements Runnable{
  14.  
    boolean run=true; //通过引入一个布尔类型的变量,来标记该线程的状态(运行/停止)
  15.  
    @Override
  16.  
    public void run() {
  17.  
    for (int i = 1; i < 1000; i++) {
  18.  
    if (run) {
  19.  
    System.out.println(Thread.currentThread().getName() + "-->" + i + "秒");
  20.  
    try {
  21.  
    Thread.sleep(1000);
  22.  
    } catch (InterruptedException e) {
  23.  
    e.printStackTrace();
  24.  
    }
  25.  
    }else{
  26.  
    /**
  27.  
    * return就表示该线程结束了
  28.  
    * 如果有什么需要保存的,可以写在return之前
  29.  
    */
  30.  
    return;
  31.  
    }
  32.  
    }
  33.  
    }
  34.  
    }

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

 

4、线程调度

4.1、常见的线程调度模型

抢占式调度模型、均分式调度模型

4.2、java中提供的线程调度方法

  1.  
    void setPriority(int newPriority)       //设置线程优先级
  2.  
     
  3.  
    int getPriority()       //获取线程优先级

最低优先级:1   默认优先级:5   最高优先级:10

优先级高的线程抢占的CPU时间片就多一些,处于运行状态的时间片就多一些

  1.  
    public class Thread_06 {
  2.  
    public static void main(String[] args) {
  3.  
    Thread_06_1 t61=new Thread_06_1();
  4.  
    Thread_06_2 t62=new Thread_06_2();
  5.  
    Thread_06_3 t63=new Thread_06_3();
  6.  
    Thread thread1=new Thread(t61,"Thread_06_1");
  7.  
    Thread thread2=new Thread(t62,"Thread_06_2");
  8.  
    Thread thread3=new Thread(t63,"Thread_06_3");
  9.  
    //设置线程优先级
  10.  
    thread1.setPriority(1); thread2.setPriority(2); thread3.setPriority(10);
  11.  
    thread1.start(); thread2.start(); thread3.start();
  12.  
    System.out.println("主线程优先级为:"+Thread.currentThread().getPriority());
  13.  
    for(int i=0;i<1000;i++){
  14.  
    System.out.println(Thread.currentThread().getName()+"-->"+i);
  15.  
    }
  16.  
    }
  17.  
    }
  18.  
    class Thread_06_1 implements Runnable{
  19.  
    @Override
  20.  
    public void run() {
  21.  
    System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
  22.  
    for(int i=0;i<1000;i++){
  23.  
    System.out.println(Thread.currentThread().getName()+"-->"+i);
  24.  
    }
  25.  
    }
  26.  
    }
  27.  
     
  28.  
    class Thread_06_2 implements Runnable{
  29.  
    @Override
  30.  
    public void run() {
  31.  
    System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
  32.  
    for(int i=0;i<1000;i++){
  33.  
    System.out.println(Thread.currentThread().getName()+"-->"+i);
  34.  
    }
  35.  
    }
  36.  
    }
  37.  
     
  38.  
    class Thread_06_3 implements Runnable{
  39.  
    @Override
  40.  
    public void run() {
  41.  
    System.out.println(Thread.currentThread().getName()+"优先级为:"+Thread.currentThread().getPriority());
  42.  
    for(int i=0;i<1000;i++){
  43.  
    System.out.println(Thread.currentThread().getName()+"-->"+i);
  44.  
    }
  45.  
    }
  46.  
    }

4.3、线程让步

yield方法:使当前线程暂停,回到就绪状态,让给其他线程

public static native void yield();
  1.  
    public class Thread_07 {
  2.  
    public static void main(String[] args) {
  3.  
    Thread_07_1 tt=new Thread_07_1();
  4.  
    Thread thread=new Thread(tt,"Thread_07_1");
  5.  
    thread.start();
  6.  
    for(int i=0;i<100;i++){
  7.  
    System.out.println(Thread.currentThread().getName()+"-->"+i);
  8.  
    }
  9.  
    }
  10.  
    }
  11.  
    class Thread_07_1 implements Runnable{
  12.  
     
  13.  
    @Override
  14.  
    public void run() {
  15.  
    for(int i=0;i<100;i++){
  16.  
    if(i%10==0){
  17.  
    System.out.println(Thread.currentThread().getName()+"暂停了一下");
  18.  
    Thread.yield(); //让当前线程暂停一下
  19.  
    }
  20.  
    System.out.println(Thread.currentThread().getName()+"-->"+i);
  21.  
    }
  22.  
    }
  23.  
    }

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

4.4、线程合并

join方法可以使得在t.join()中让CPU优先执行完t。将t合并到当前线程中,使当前线程受阻,t线程执行直到结束。

  1.  
    public class Thread_08 {
  2.  
    public static void main(String[] args) throws InterruptedException {
  3.  
    Thread_08_1 thread_08_1=new Thread_08_1();
  4.  
    Thread_08_2 thread_08_2=new Thread_08_2();
  5.  
    Thread thread=new Thread(thread_08_1,"Thread_08_1");
  6.  
    Thread thread1=new Thread(thread_08_2,"Thread_08_2");
  7.  
    thread.start();
  8.  
    //合并线程
  9.  
    thread.join(); //t合并到当前线程中,当前线程受阻塞,t线程执行直到结束
  10.  
    thread1.start();
  11.  
    thread1.join();
  12.  
    //Thread.currentThread().join();
  13.  
    System.out.println("main over");
  14.  
    }
  15.  
    }
  16.  
    class Thread_08_1 implements Runnable{
  17.  
     
  18.  
    @Override
  19.  
    public void run() {
  20.  
    for(int i=0;i<3;i++){
  21.  
    System.out.println(Thread.currentThread().getName()+"-->"+i);
  22.  
    }
  23.  
    }
  24.  
    }
  25.  
    class Thread_08_2 implements Runnable{
  26.  
     
  27.  
    @Override
  28.  
    public void run() {
  29.  
    for(int i=0;i<3;i++){
  30.  
    System.out.println(Thread.currentThread().getName()+"-->"+i);
  31.  
    }
  32.  
    }
  33.  
    }

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

join()的底层实现代码。

  1.  
    public final void join() throws InterruptedException {
  2.  
    join(0);
  3.  
    }
  4.  
     
  5.  
     
  6.  
    public final synchronized void join(long millis)
  7.  
    throws InterruptedException {
  8.  
    long base = System.currentTimeMillis();
  9.  
    long now = 0;
  10.  
     
  11.  
    if (millis < 0) {
  12.  
    throw new IllegalArgumentException("timeout value is negative");
  13.  
    }
  14.  
     
  15.  
    if (millis == 0) {
  16.  
    while (isAlive()) {
  17.  
    wait(0);
  18.  
    }
  19.  
    } else {
  20.  
    while (isAlive()) {
  21.  
    long delay = millis - now;
  22.  
    if (delay <= 0) {
  23.  
    break;
  24.  
    }
  25.  
    wait(delay);
  26.  
    now = System.currentTimeMillis() - base;
  27.  
    }
  28.  
    }
  29.  
    }

join()是在底层调用了wait方法,当主线程调用了thread.join()之后,主线程进入此方法,调用join()方法中的wait(0)方法,wait(0)表示无限等待直到被notify。即主线程会无限等待thread线程执行完成。

  1.  
    public final void wait(long timeout, int nanos) throws InterruptedException {
  2.  
    if (timeout < 0) {
  3.  
    throw new IllegalArgumentException("timeout value is negative");
  4.  
    }
  5.  
     
  6.  
    if (nanos < 0 || nanos > 999999) {
  7.  
    throw new IllegalArgumentException(
  8.  
    "nanosecond timeout value out of range");
  9.  
    }
  10.  
     
  11.  
    if (nanos > 0) {
  12.  
    timeout++;
  13.  
    }
  14.  
     
  15.  
    wait(timeout);
  16.  
    }

4.5、线程安全

数据在多线程并发的环境下会存在安全问题。例如如果多个用户想要修改某个共享的数据,就会引发线程安全问题。因此需要引入线程同步机制(即线程排队执行,不能并发)

4.5.1、线程同步的实现

java里面通过关键字synchronized给线程加锁。线程会获取锁,并独占cpu,只有当线程释放了锁之后,其余线程拿到锁之后才能运行。

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法当一个线程在运行状态时遇到synchronized关键字,该线程就会放弃占有的cpu时间片,在锁池里面找共享对象的对象锁。

一个线程同步synchronized的例子——模拟ATM机取款

Account类
  1.  
    package ATM;
  2.  
    /*
  3.  
    银行账户,使用线程同步机制,解决线程安全问题
  4.  
    */
  5.  
    public class Account {
  6.  
    private String accountName; //账户名
  7.  
    private double remain; //余额
  8.  
    Object obj=new Object();
  9.  
     
  10.  
    public Account(String accountName, int remain) {
  11.  
    this.accountName = accountName;
  12.  
    this.remain = remain;
  13.  
    }
  14.  
     
  15.  
    public String getAccountName() {
  16.  
    return accountName;
  17.  
    }
  18.  
     
  19.  
    public void setAccountName(String accountName) {
  20.  
    this.accountName = accountName;
  21.  
    }
  22.  
     
  23.  
    public double getRemain() {
  24.  
    return remain;
  25.  
    }
  26.  
     
  27.  
    public void setRemain(double remain) {
  28.  
    this.remain = remain;
  29.  
    }
  30.  
    //取款方法
  31.  
    public void withdraw(double money) {
  32.  
    //synchronized (obj){ //obj是一个全局变量,实例一次Account创建一个obj对象,是可以被共享的
  33.  
     
  34.  
    /*Object obj1=new Object();
  35.  
    synchronized (obj1){*/ //obj1是一个局部变量,每调用一次run方法则会创建一个obj1对象,所以obj1不是共享对象
  36.  
     
  37.  
    //synchronized (null){ //报错:空指针
  38.  
    //synchronized ("abc"){ //"abc"在字符串常量池当中,会让所有的线程都同步
  39.  
    synchronized (this){
  40.  
    double before = this.getRemain(); //取款之前的余额
  41.  
    double after = before - money; //取款之后的余额
  42.  
    try{
  43.  
    Thread.sleep(1000);
  44.  
    } catch (InterruptedException e) {
  45.  
    e.printStackTrace();
  46.  
    }
  47.  
    this.setRemain(after); //更新余额
  48.  
    }
  49.  
    }
  50.  
    }
AccountThread类
  1.  
    package ATM;
  2.  
     
  3.  
    public class AccountThread extends Thread {
  4.  
    //两个线程必须共享同一个账户对象
  5.  
    private Account act;
  6.  
    private double money; //取款金额
  7.  
     
  8.  
    //通过构造方法传递过来构造对象
  9.  
    public AccountThread(Account act,double money) {
  10.  
    this.act = act;
  11.  
    this.money=money;
  12.  
    }
  13.  
    @Override
  14.  
    public void run() { //run方法执行表示取款操作
  15.  
    act.withdraw(money);
  16.  
    System.out.println(Thread.currentThread().getName()+"在账户"+
  17.  
    act.getAccountName()+"取款"+money+"之后余额为:"+act.getRemain());
  18.  
    }
  19.  
    }

启动类:Test类

  1.  
    package ATM;
  2.  
     
  3.  
    public class Test {
  4.  
    public static void main(String[] args) {
  5.  
    Account act=new Account("act001",10000); //创建账户对象
  6.  
     
  7.  
    //创建两个线程,对同一账户取款
  8.  
    Thread threadA=new AccountThread(act,5000);
  9.  
    Thread threadB=new AccountThread(act,3000);
  10.  
     
  11.  
    threadA.setName("小明");
  12.  
    threadB.setName("小华");
  13.  
     
  14.  
    threadA.start();
  15.  
    threadB.start();
  16.  
    }
  17.  
    }

结果:

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

  1.  
    //synchronized可以用在实例方法上,此时锁是this
  2.  
    public synchronized void withdraw(double money) {
  3.  
    double before = this.getRemain(); //取款之前的余额
  4.  
    double after = before - money; //取款之后的余额
  5.  
    try {
  6.  
    Thread.sleep(1000);
  7.  
    } catch (InterruptedException e) {
  8.  
    e.printStackTrace();
  9.  
    }
  10.  
    this.setRemain(after); //更新余额
  11.  
    }
  12.  
    }

可以在实例方法上使用synchronized,synchronized出现在实例方法上,锁一定是this,不能是其他的对象了。所以这种方式不灵活。

另外还有一个缺点:synchronized出现在实例方法上,表示整个方法体都需要同步,可能会无故扩大同步的范围,导致程序的执行效率降低。所以这种方式不常用。

4.5.2、java中的线程安全性

java中有三大变量 :1、实例变量(在堆中)  2、静态变量(在方法中) 3、局部变量(在栈中)

局部变量不会有线程安全问题,因为局部变量不共享,局部变量在栈中,一个线程一个栈。

常量不会有线程安全问题,因为常量不会被改变。

实例变量在堆中,堆只有1个。静态变量在方法区中,方法区只有1个。由于堆和方法区都是多线程可共享的,所以实例变量和静态变量可能存在线程安全问题。

ArrayList、HashMap 、HashSet 是非线程安全的

Vector、Hashtable 是线程安全的

4.5.3、synchronized总结

synchronized有三种写法:

第一种:同步代码块(灵活)

synchronized(线程共享对象){

      同步代码块;

}

第二种:在实例方法上使用 synchronized

表示共享对象一定是this,并且同步代码块是整个方法体。

第三种:在静态方法上使用 synchronized

表示找类锁。

类锁永远只有1把(就算创建了100个对象,那类锁也只有1把)

对象锁:1个对象1把锁,100个对象100把锁。 类锁:100个对象,也可能只是1把类锁。

  1.  
    package Synchronized;
  2.  
     
  3.  
    //Q:doOther方法执行的时候需要等待doSome方法的结束吗?
  4.  
    //A:需要,因为静态方法是类锁,不管创建了几个对象,类锁只有1把
  5.  
    public class Exam01 {
  6.  
    public static void main(String[] args) throws InterruptedException {
  7.  
    MyClass mc1 = new MyClass();
  8.  
    MyClass mc2 = new MyClass();
  9.  
    Thread t1 = new Mythread(mc1);
  10.  
    Thread t2 = new Mythread(mc2);
  11.  
    t1.setName("t1");
  12.  
    t2.setName("t2");
  13.  
    t1.start();
  14.  
    Thread.sleep(1000);
  15.  
    t2.start();
  16.  
    }
  17.  
    }
  18.  
     
  19.  
    class Mythread extends Thread {
  20.  
    private MyClass mc;
  21.  
     
  22.  
    public Mythread(MyClass mc) {
  23.  
    this.mc = mc;
  24.  
    }
  25.  
     
  26.  
    @Override
  27.  
    public void run() {
  28.  
    super.run();
  29.  
    if (Thread.currentThread().getName().equals("t1")) {
  30.  
    try {
  31.  
    mc.doSome();
  32.  
    } catch (InterruptedException e) {
  33.  
    e.printStackTrace();
  34.  
    }
  35.  
    }
  36.  
    if (Thread.currentThread().getName().equals("t2")) {
  37.  
    mc.doOther();
  38.  
    }
  39.  
    }
  40.  
    }
  41.  
     
  42.  
    class MyClass {
  43.  
    //synchronized出现在静态方法上是走 类锁
  44.  
    public synchronized static void doSome() throws InterruptedException {
  45.  
    System.out.println("doSome begin");
  46.  
    Thread.sleep(1000 * 5);
  47.  
    System.out.println("doSome over");
  48.  
    }
  49.  
     
  50.  
    public synchronized static void doOther() {
  51.  
    System.out.println("doOther begin");
  52.  
    System.out.println("doOther over");
  53.  
    }
  54.  
    }

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

4.5.4、死锁

一个简单的死锁实现的例子

  1.  
    package Thread;
  2.  
     
  3.  
    /*
  4.  
    实现一个死锁,
  5.  
    t1中obj1锁上后就睡了,t2中obj2锁上后就睡了。
  6.  
    t1想要释放obj1锁,就必须请求到obj2锁
  7.  
    t2想要释放obj2锁,就必须请求到obj1锁
  8.  
    t1与t2互相请求锁,但彼此都无法释放锁,所以形成了死锁
  9.  
     
  10.  
    */
  11.  
    public class Dead_lock {
  12.  
    public static void main(String[] args) {
  13.  
    Object obj1 = new Object();
  14.  
    Object obj2 = new Object();
  15.  
     
  16.  
    //t1,t2两个线程共享o1,o2
  17.  
    Thread t1 = new MyThread_1(obj1, obj2);
  18.  
    Thread t2 = new MyThread_2(obj1, obj2);
  19.  
    t1.start();
  20.  
    t2.start();
  21.  
    }
  22.  
    }
  23.  
     
  24.  
    class MyThread_1 extends Thread {
  25.  
    Object obj1 = new Object();
  26.  
    Object obj2 = new Object();
  27.  
     
  28.  
    public MyThread_1(Object obj1, Object obj2) {
  29.  
    this.obj1 = obj1;
  30.  
    this.obj2 = obj2;
  31.  
    }
  32.  
     
  33.  
    @Override
  34.  
    public void run() {
  35.  
    synchronized (obj1) {
  36.  
    try {
  37.  
    Thread.sleep(1000 * 3);
  38.  
    } catch (InterruptedException e) {
  39.  
    e.printStackTrace();
  40.  
    }
  41.  
    synchronized (obj2) {
  42.  
     
  43.  
    }
  44.  
    }
  45.  
    }
  46.  
    }
  47.  
     
  48.  
    class MyThread_2 extends Thread {
  49.  
    Object obj1 = new Object();
  50.  
    Object obj2 = new Object();
  51.  
     
  52.  
    public MyThread_2(Object obj1, Object obj2) {
  53.  
    this.obj1 = obj1;
  54.  
    this.obj2 = obj2;
  55.  
    }
  56.  
     
  57.  
    @Override
  58.  
    public void run() {
  59.  
    synchronized (obj2) {
  60.  
    try {
  61.  
    Thread.sleep(1000 * 3);
  62.  
    } catch (InterruptedException e) {
  63.  
    e.printStackTrace();
  64.  
    }
  65.  
    synchronized (obj1) {
  66.  
     
  67.  
    }
  68.  
    }
  69.  
    }
  70.  
    }
  71.  
     

注:synchronized在开发中最好不要嵌套使用,一不小心就可能导致死锁现象发生

4.6、守护线程

java语言中线程可分为两大类:

一类是:用户线程       例如:main方法主线程

 一类是:守护线程(后台线程)    例如java垃圾回收线程

4.6.1、守护线程的特点

一般守护线程是一个死循环,所有用户线程只要结束,守护线程自动结束。

守护线程一般会用在一些定时任务,例如每天0点系统自动备份需要用到定时器,我们可以将定时器设置为守护线程一直在那里看着。每到0点就备份一次。所有的用户线程如果结束了,守护线程就自动退出。

4.6.2、一个简单的守护线程的例子

  1.  
    package Thread;
  2.  
     
  3.  
    /**
  4.  
    * 用户线程备份数据,守护线程守护。
  5.  
    */
  6.  
    public class GuardThread {
  7.  
    public static void main(String[] args) {
  8.  
    Thread thread = new BakDataThread();
  9.  
    thread.setName("备份数据的线程");
  10.  
     
  11.  
    //启动线程之前,将线程设置为守护线程
  12.  
    thread.setDaemon(true);
  13.  
    thread.start();
  14.  
    for (int i = 0; i < 5; i++) {
  15.  
    System.out.println(Thread.currentThread().getName() + "--->" + i);
  16.  
    try {
  17.  
    Thread.sleep(1000);
  18.  
    } catch (InterruptedException e) {
  19.  
    e.printStackTrace();
  20.  
    }
  21.  
    }
  22.  
    }
  23.  
    }
  24.  
     
  25.  
    class BakDataThread extends Thread {
  26.  
    @Override
  27.  
    public void run() {
  28.  
    int i = 0;
  29.  
    //即使是死循环,但由于该线程是守护者。当用户线程结束,守护线程自动终止
  30.  
    while (true) {
  31.  
    System.out.println(Thread.currentThread().getName() + "--->" + i++);
  32.  
    try {
  33.  
    Thread.sleep(1000);
  34.  
    } catch (InterruptedException e) {
  35.  
    e.printStackTrace();
  36.  
    }
  37.  
    }
  38.  
    }
  39.  
    }
  40.  
     

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

 

5、定时任务

5.1、实现一个定时器

  1.  
    package Thread;
  2.  
     
  3.  
    import java.text.ParseException;
  4.  
    import java.text.SimpleDateFormat;
  5.  
    import java.util.Date;
  6.  
    import java.util.Timer;
  7.  
    import java.util.TimerTask;
  8.  
     
  9.  
    public class TimerTest {
  10.  
    public static void main(String[] args) throws ParseException {
  11.  
    //创建定时器对象
  12.  
    Timer timer = new Timer();
  13.  
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  14.  
    Date firstTime = sdf.parse("2020-07-25 19:10:30");
  15.  
    //指定定时任务
  16.  
    //timer.schedule(定时任务,第一次执行时间时间,间隔多久执行一次)
  17.  
    timer.schedule(new LogTimerTask(), firstTime, 1000 * 5);
  18.  
    }
  19.  
    }
  20.  
     
  21.  
    //编写一个定时任务
  22.  
    class LogTimerTask extends TimerTask {
  23.  
     
  24.  
    @Override
  25.  
    public void run() {
  26.  
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  27.  
    String strTime = sdf.format(new Date());
  28.  
    System.out.println(strTime + ":成功完成了一次数据备份!");
  29.  
    }
  30.  
    }

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

6、通过Callable接口实现一个线程

使用Callable接口实现线程,可以获得该线程的返回值(JDK8新特性)

一个简单的实例:

  1.  
    package Thread;
  2.  
     
  3.  
    import java.util.concurrent.Callable;
  4.  
    import java.util.concurrent.ExecutionException;
  5.  
    import java.util.concurrent.FutureTask;
  6.  
     
  7.  
    public class ThirdWay {
  8.  
    public static void main(String[] args) throws ExecutionException, InterruptedException {
  9.  
    FutureTask futureTask = new FutureTask(new Task());
  10.  
    Thread thread = new Thread(futureTask);
  11.  
    thread.start();
  12.  
    Object object = futureTask.get(); //通过get方法可以获取当前线程的返回值
  13.  
    ////主线程中这里的程序必须等待get()方法结束才执行
  14.  
    // get()方法为了拿另一个线程的执行结果需要等待其执行完成,因此要等待较长时间
  15.  
    System.out.print("线程执行结果:");
  16.  
    System.out.println(object);
  17.  
    }
  18.  
    }
  19.  
     
  20.  
    class Task implements Callable {
  21.  
     
  22.  
    @Override
  23.  
    public Object call() throws Exception {
  24.  
    System.out.println("call method begin");
  25.  
    Thread.sleep(1000 * 10);
  26.  
    System.out.println("call method end");
  27.  
    int a = 100;
  28.  
    int b = 200;
  29.  
    return a + b;
  30.  
    }
  31.  
    }

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

FutureTask类相关源码
  1.  
    public FutureTask(Callable<V> callable) {
  2.  
    if (callable == null)
  3.  
    throw new NullPointerException();
  4.  
    this.callable = callable;
  5.  
    this.state = NEW; // ensure visibility of callable
  6.  
    }
  1.  
     
  2.  
    public V get() throws InterruptedException, ExecutionException {
  3.  
    int s = state;
  4.  
    if (s <= COMPLETING)
  5.  
    s = awaitDone(false, 0L);
  6.  
    return report(s);
  7.  
    }

Future类中实现了get方法获取传入线程的返回结果

7、Object类中的wait和notify方法

7.1、wait和notify方法介绍

wait和notify方法不是线程对象的方法,不能通过线程对象调用。

  1.  
    Object object=new Object();
  2.  
    object.wait();//object.wait()让正在object对象上活动的线程进入等待状态,无限等待,直到被唤醒为止
  3.  
    object.notify();//object.notify()唤醒正在object对象上等待的线程
  4.  
    object.notifyAll();//object.notifyAll唤醒正在object对象上等待的所有线程

7.2、生产者和消费者模式

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

一个简单的生产者和消费者实例

模拟生产者生产一个,消费者就消费一个。让仓库始终零库存。

  1.  
    package Thread;
  2.  
     
  3.  
    import java.awt.*;
  4.  
    import java.util.*;
  5.  
    import java.util.List;
  6.  
     
  7.  
    public class Producer_Consumer {
  8.  
    public static void main(String[] args) {
  9.  
    List<String> list = new ArrayList<String>();
  10.  
    Thread thread1 = new Thread(new Consumer(list), "消费者线程");
  11.  
    Thread thread2 = new Thread(new Producer(list), "生产者线程");
  12.  
    thread1.start();
  13.  
    thread2.start();
  14.  
    }
  15.  
    }
  16.  
     
  17.  
    //消费者线程
  18.  
    class Consumer implements Runnable {
  19.  
    //仓库
  20.  
    private List<String> list;
  21.  
     
  22.  
    public Consumer(List<String> list) {
  23.  
    this.list = list;
  24.  
    }
  25.  
     
  26.  
    @Override
  27.  
    public void run() {
  28.  
    //消费
  29.  
    while (true) {
  30.  
    synchronized (list) {
  31.  
    //如果仓库已经空了,消费者线程等待并释放list集合的锁
  32.  
    if (list.size() == 0) {
  33.  
    try {
  34.  
    list.wait();
  35.  
    } catch (InterruptedException e) {
  36.  
    e.printStackTrace();
  37.  
    }
  38.  
    }
  39.  
    //仓库中有商品,消费者进行消费
  40.  
    String str = list.remove(0);
  41.  
    System.out.println(Thread.currentThread().getName() + " 消费 " + str);
  42.  
    list.notify(); //唤醒生产者
  43.  
    }
  44.  
     
  45.  
    }
  46.  
    }
  47.  
    }
  48.  
     
  49.  
    //生产者线程
  50.  
    class Producer implements Runnable {
  51.  
    //仓库
  52.  
    private List<String> list;
  53.  
     
  54.  
    public Producer(List<String> list) {
  55.  
    this.list = list;
  56.  
    }
  57.  
     
  58.  
    @Override
  59.  
    public void run() {
  60.  
    //生产
  61.  
    while (true) {
  62.  
    synchronized (list) {
  63.  
    //如果仓库里有东西,则停止生产。生产者线程等待并释放list集合的锁
  64.  
    if (list.size() > 0) {
  65.  
    try {
  66.  
    list.wait();
  67.  
    } catch (InterruptedException e) {
  68.  
    e.printStackTrace();
  69.  
    }
  70.  
    }
  71.  
    list.add("商品");
  72.  
    System.out.println(Thread.currentThread().getName() + " 生产 " + list.get(0));
  73.  
    list.notify(); //唤醒消费者
  74.  
    }
  75.  
    }
  76.  
    }
  77.  
    }
  78.  
     

java线程基础知识整理
线程基本概念
1、java实现线程
2、线程的生命周期
3、线程常用的方法
 
4、线程调度
4.5、线程安全
 
5、定时任务
6、通过Callable接口实现一个线程
7、Object类中的wait和notify方法

7.3、实现奇偶数的交替输出

  1.  
    package Thread;
  2.  
     
  3.  
    /**
  4.  
    * 使用生产者和消费者模式实现两个线程交替输出:一个线程负责输出奇数,另一个线程负责输出偶数
  5.  
    */
  6.  
    public class Number {
  7.  
    public static void main(String[] args) throws InterruptedException {
  8.  
    Num num = new Num(0);
  9.  
    Thread thread1 = new Thread(new Odd(num), "Odd");
  10.  
    Thread thread2 = new Thread(new Event(num), "Event");
  11.  
    thread1.start();
  12.  
    thread2.start();
  13.  
    }
  14.  
    }
  15.  
     
  16.  
    class Odd implements Runnable {
  17.  
    Num num;
  18.  
     
  19.  
    public Odd(Num num) {
  20.  
    this.num = num;
  21.  
    }
  22.  
     
  23.  
    @Override
  24.  
    public void run() {
  25.  
    while (true) {
  26.  
    synchronized (num) {
  27.  
    try {
  28.  
    Thread.sleep(1000);
  29.  
    } catch (InterruptedException e) {
  30.  
    e.printStackTrace();
  31.  
    }
  32.  
    if (num.getI() % 2 == 0) {
  33.  
    try {
  34.  
    num.wait();
  35.  
    } catch (InterruptedException e) {
  36.  
    e.printStackTrace();
  37.  
    }
  38.  
    }
  39.  
    System.out.println(Thread.currentThread().getName() + "--->" + num.printNum());
  40.  
    num.notifyAll();
  41.  
    }
  42.  
    }
  43.  
    }
  44.  
    }
  45.  
     
  46.  
    class Event implements Runnable {
  47.  
    Num num;
  48.  
     
  49.  
    public Event(Num num) {
  50.  
    this.num = num;
  51.  
    }
  52.  
     
  53.  
    @Override
  54.  
    public void run() {
  55.  
    while (true) {
  56.  
    synchronized (num) {
  57.  
    try {
  58.  
    Thread.sleep(1000);
  59.  
    } catch (InterruptedException e) {
  60.  
    e.printStackTrace();
  61.  
    }
  62.  
    if (num.getI() % 2 != 0) {
  63.  
    try {
  64.  
    num.wait();
  65.  
    } catch (InterruptedException e) {
  66.  
    e.printStackTrace();
  67.  
    }
  68.  
    }
  69.  
    System.out.println(Thread.currentThread().getName() + "--->" + num.printNum());
  70.  
    num.notifyAll();
  71.  
    }
  72.  
    }
  73.  
    }
  74.  
    }
  75.  
     
  76.  
    class Num {
  77.  
    private int i = 0;
  78.  
     
  79.  
    public Num(int i) {
  80.  
    this.i = i;
  81.  
    }
  82.  
     
  83.  
    public int getI() {
  84.  
    return i;
  85.  
    }
  86.  
     
  87.  
    public void setI(int i) {
  88.  
    this.i = i;
  89.  
    }
  90.  
     
  91.  
    int printNum() {
  92.  
    return i++;
  93.  
    }
  94.