Java多线程学习笔记

49 篇文章 1 订阅
43 篇文章 0 订阅

Java多线程学习笔记

1、继承Thread类

实现多线程的常用方法就是继承Thread类,然后覆盖run()方法。下面是一个通过继承Thread类实现多线程的小例子:

import java.util.Date;

public class ThreadTest extends Thread{

    @Override
    public void run() { // 重写run方法
        System.out.println(this.getName() + " " + new Date().toString());
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ThreadTest t1 = new ThreadTest();
        t1.start();
        ThreadTest t2 = new ThreadTest();
        t2.start();
    }
}

运行结果如下:

这里写图片描述

Thread类实际上是Runnable接口的实现,

public class Thread implements Runnable {
    ...
}

Thread类中,有一些方法比较常用:

  1. 线程在创建后,需要调用start()才能运行,然后线程会自动调用run()方法,所以在子类中需要覆盖run()方法。注意,我们要用start()来启动线程,run()方法仅仅是一个普通的方法。

  2. sleep()和suspend()都可以让线程暂停执行,但是二者的区别如下:
    (1).sleep()使线程休眠后,只能在时间结束后,线程才能恢复到就绪状态;而suspend()挂起线程后,可以通过resume()唤醒线程。
    (2).另外,sleep()仅对当前线程起作用,而suspend()可以挂起另一个线程。

  3. yield()是一个静态的native方法,它的作用是“告诉当前正在执行的线程,把运行机会交给线程池中拥有相同优先级的线程”,说白了就是把机会留给别人。

  4. stop()和interrupt()都可以中断线程,但二者区别在于stop强制终止线程,而interrupt()中断线程。关于二者的具体介绍,详见:http://blog.csdn.net/wxwzy738/article/details/8516253。这里插一句,如果我们要中断线程,最好设置一个共享变量,然后在run方法中,周期性地检查该变量的值,满足终止条件时再终止线程。

为了加深理解,现附上线程生命周期图(图片来自百度百科):

这里写图片描述

2、实现Runnable接口

Runnable接口的源码如下,只有一个抽象方法run(),因此实现这个接口的核心就是覆盖run()方法。

public interface Runnable {
    public abstract void run();
}

下面是一个小例子,这个例子实现了Runnable接口

import java.util.Date;

public class ThreadTest implements Runnable{

    String name;

    ThreadTest(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(this.name + " " + new Date().toString());
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ThreadTest t1 = new ThreadTest("Thread1");
        new Thread(t1).start();
        ThreadTest t2 = new ThreadTest("Thread2");
        new Thread(t2).start();
    }

}

运行结果如下:

这里写图片描述

3、join方法

无join()时,主线程调用start()后,程序立即返回到主线程,此时主线程和子线程各运行各的,也就是我们常说的“异步”。

有join()时,主线程调用start()后,会一直等待子线程执行完毕,再往下执行,这也就是“同步”。

下面看两个小例子:

import java.util.Date;

public class ThreadTest extends Thread{

    @Override
    public void run() {
        while(true){
            try {
                this.sleep(1000);
                System.out.println(this.getName() + " " + new Date().toString());
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }   
        }
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ThreadTest t1 = new ThreadTest();
        t1.start();        
        ThreadTest t2 = new ThreadTest();
        t2.start();

        // 没有join() 直接返回到主线程
        System.out.println("t1,t2 stop");
    }

}

运行结果显示,主线程在启动t1、t2两个子线程后,直接返回到主线程继续执行 System.out.println(“t1,t2
stop”);

这里写图片描述

下面我们加上join()方法,同时把while去掉,避免程序进入死循环无法返回。

import java.util.Date;

public class ThreadTest extends Thread{

    @Override
    public void run() {
        // 去掉了while(true)的循环
        try {
            this.sleep(1000);
            System.out.println(this.getName() + " " + new Date().toString());
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
    }

    public static void main(String[] args) throws InterruptedException { // 注意要抛出异常
        // TODO Auto-generated method stub
        ThreadTest t1 = new ThreadTest();
        t1.start();        
        ThreadTest t2 = new ThreadTest();
        t2.start();
        t1.join(); // t1添加join()
        t2.join(); // t2添加join()
        System.out.println("t1,t2 stop");
    }

}

来看看效果吧,可以看到,程序直到t1、t2线程结束后,才返回执行System.out.println(“t1,t2 stop”);

这里写图片描述

4、synchronized关键字

synchronized知识点总结:

1、synchronized可以修饰代码块、对象和方法,不能用于修饰int、char、boolean等简单类型的变量。

synchronized int a = 0; // 错误,synchronized 不能修饰int类型变量

2、synchronized不能被继承。若父类中有一个方法使用了synchronized修饰,子类中覆盖了这个方法,子类中的这个方法默认不是同步的,必须加synchronized关键词显示地生命同步。

3、synchronized不能放在返回类型后面,比如下面这句代码就是错误的。

void synchronized func() // 错的,synchronized不能放在返回值后面

synchronized关键字可以修饰代码块:

class Test {
    private final static String key = ""; // 注意要设置为final

    synchronized(key) {
        // TODO 这里是同步的代码块
        // 注意,这里不能改变key的值,否则不能同步
        // 因此要把key设置为final
    }
}

synchronized关键字也可以用来修饰方法,请看下面的例子:在单件模式中使用synchronized关键字。

5、用synchronized关键字实现单件模式

class Singleton {
    private static Singleton instance;
    private Singleton() {
    }

    // 使用synchronized 关键字修饰方法
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

当然,单件模式还有更简单的实现方式:

public class Singleton {
    private final static Singleton instance = new Singleton(); // 使用“急切”
    private Singleton() {
    }
    public static Singleton getInstance() {
         return instance;
    }

}

6、实例:使用多线程打印数字

public class ThreadTest implements Runnable {

    private int num = 0;

    @Override
    public synchronized void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println(this.num);
            num++;
        }
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ThreadTest t1 = new ThreadTest();
        new Thread(t1).start(); // 打印0-9
        new Thread(t1).start(); // 打印10-19
        new Thread(t1).start(); // 打印20-29
    }

}

程序的运行结果如下:

这里写图片描述

今天就到这里,拜~

这里写图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值