前言:想成为一名优秀的程序员,线程这个重要的知识点一定要掌握。
一、什么是线程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。——百度百科
撇开调度不说,进程就是应用程序在内存中分配的空间,java启动main方法就是启动了一个JVM的进程,JVM内存模型如图,而main方法又是此进程中的一个线程,又称主线程。
例:把内存比作一块地,春天了,赵家村要搞一次种地运动,村长为南区分配一块地,配置水源,工具区。张三兴致勃勃的去种地了。
如果种地任务太多,靠张三一个人速度会很慢,把张三累死了,这次运动也就失败了,因此就需要加一些人共同来种,大家每人一亩,互不影响,这样效率就高很多了。
此场景中线程就是一个种地的人。
二、线程的创建与运行
1. 继承Thread类
public class ThreadDemo {
public static class TillThread extends Thread {
@Override
public void run() {
//分配任务
System.out.println("种地");
}
}
public static void main(String[] args) {
//创建一个线程
Thread 张三 = new TillThread();
//开始执行任务
张三.start();
}
}
2. 实现Runnable接口
public class ThreadDemo {
public static class TillThread implements Runnable {
@Override
public void run() {
//分配任务
System.out.println("种地");
}
}
public static void main(String[] args) {
//创建一个任务
TillThread 种地 = new TillThread();
//创建一个线程
Thread 张三 = new Thread(种地);
//开始执行任务
张三.start();
}
}
3. 实现Callable类
public class ThreadDemo {
public static class TillThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("种地");
return "种了一亩地";
}
}
public static void main(String[] args) {
//创建一个任务
TillThread tillThread = new TillThread();
FutureTask<String> tillTask = new FutureTask<>(tillThread);
//创建一个线程
Thread 张三 = new Thread(tillTask);
张三.start();
try {
String result = tillTask.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
这种方式可以获取到这个线程的返回值。
4. 如何选择
我们可以看到Thread也是实现的Runnable接口,考虑到java“单继承,多实现”的特性,使用实现Runnable接口的方式更加灵活。
三、常用的方法
- Thread.currentThread(); ——获取正在执行的线程对象的引用。
- Thread.sleep(1000); ——使当前线程睡眠1秒。
- thread.setName(); ——设置线程的名字。
- thread.getName(); ——获取线程的名字。
- thread.start(); ——执行线程的任务。
- thread.join(); ——使当前线程等待thread执行完毕后在执行。
多线程编程中常用的wait()和notify()是Object中的方法。
我们通常使用Synchronized关键字给某一段代码上锁。被上锁的代码块同一时间只能有一个线程执行。
还是上面的例子,麦子熟了,但是南区只有一台收割机,这个时候如果张三和李四都使用那肯定是没法实现的,所以只能张三收割完李四再收割了。可以说当有资源需要排队使用时需要使用到锁。
Java多线程的锁都是基于对象的,Java中的每一个对象都可以作为一个锁。
// 关键字在实例方法上,锁为当前实例
public synchronized void instanceLock() {
}
// 关键字在静态方法上,锁为当前Class对象
public static synchronized void classLock() {
}
// 关键字在代码块上,锁为括号里面的对象
public void blockLock() {
Object o = new Object();
synchronized (o) {
}
}
注:Java类只有一个Class对象(可以有多个实例对象,多个实例共享这个Class对象),而Class对象也是特殊的Java对象。所以我们常说的类锁,其实就是Class对象的锁。
- synchronized是重量级的锁
- javaSE1.6后对synchronized进行了优化,所以我们放心使用。
五、思考
使用时上锁的三种方式该如何选择呢?jdk中那些地方用到了这几种方式呢?