一、线程是什么?
线程:Thread,是Java程序执行的发动机。就是线程运行着我们的代码。
进程:Process,一个程序就是操作系统的一个进程,一个进程下可以有很多线程。
线程的执行:线程其实就是执行一个入口方法,执行完毕就结束了。比如main方法是主线程。
线程在执行方法的时候,每次遇到方法调用,都会给当前的线程栈增加一层,这一层里保存的,就是线程当前的执行状态,比如当前方法的局部变量的值,当前方法执行到哪里了等。
所以线程栈里的每一条,都是方法已经开始执行但是还没有结束的方法,没有结束时因为它代码还没执行完,或者时在等待其调用的方法执行完。
二、创建自己的线程
打印一段文字,每打印一个字休眠
public class CreateThreadAppMain {
private static final String TEXT = "窗外的麻雀,在电线杆上多嘴,你说这一句,很多夏天的感觉,手中的铅笔,在纸上来来回回............";
public static void main(String[] args) {
// TODO 代码是被线程执行的,任何代码都可以通过Thread.currentThread()获取执行当前代码的线程
System.out.println("程序开始,执行的线程名字叫做" + Thread.currentThread().getName());
// TODO 改成2试试看?
for (int i = 1; i <= 1; i++) {
// TODO 学习创建线程的方法
// TODO Runnable接口里的run是线程执行的方法,执行完毕,线程就结束了
// TODO 理解代码是在线程里被执行的,同样的代码可以被多个线程执行。
// TODO 暂停一下 Java ,看看有多少线程,每个线程的名字等信息
Thread thread = new Thread(new PrintStoryRunnable(TEXT, 200 * i), "我的线程-" + i);
// TODO 创建好线程之后,如果要启动线程,必须调用start方法,注意不是run方法
thread.start();
}
System.out.println("启动线程结束,名字叫做" + Thread.currentThread().getName());
}
static class PrintStoryRunnable implements Runnable {
private String text;
private long interval;
public PrintStoryRunnable(String text, long interval) {
this.text = text;
this.interval = interval;
}
@Override
public void run() {
try {
double num = Math.random();
System.out.println("执行这段代码的线程名字叫做" + Thread.currentThread().getName());
printSlowly(text, interval);
System.out.println(Thread.currentThread().getName() + "执行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void printSlowly(String text, long interval) throws InterruptedException {
for (char ch : text.toCharArray()) {
Thread.sleep(interval);
System.out.print(ch);
}
System.out.println();
}
}
如果把上面代码的for循环,改成循环多次,则创建了多个线程,如果时间也是不一样的,则此时打印出来的文字是混乱的。
线程状态
三、同步控制之synchronized
synchronized关键字用来修饰成员方法,代表这个方法对于同一个对象来说,同一时间只允许一个线程执行,别的线程如果也调用这个实例的这个方法,就需要等待已经在执行这个方法的线程执行完毕,才能进入方法执行。
饿汉式的单例模式是线程安全的,因为初始化的时候只创建一次。而懒汉式的单例模式是非线程安全的,这个时候可以用到synchronized上锁:
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
// 第一次判空是保证初始化之后的所有线程,不需要等待锁
if (instance == null) {
synchronized (Singleton.class) {
// 第二次判空是多线程竞争锁,拿到锁的先初始化,后拿到锁的无需再进去初始化。(保证对象为空才去创建)
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
四、线程池
线程池(thread pool):一种线程使用模式。创建线程和销毁线程的花销是比较大的,需要多个线程时,一个一个创建,使用完了再销毁,这样带来过多的开销。而线程池可以维护多个线程,有任务的线程放入任务队列,空闲线程放入空闲队列等待任务,可以把创建和销毁的线程的过程去掉。
使用线程池的优势:
- 提高效率,创建好一定数量的线程放在池中,等需要使用的时候就从池中拿一个,这要比需要的时候创建一个线程对象要快的多。
- 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
- 提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间