线程对象实现
一、继承Thread类
创建线程的步骤:
1、定义一个类继承Thread。
2、重写run方法。
3、创建子类对象,就是创建线程对象。
4、调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。
public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName() + ":" + i); } } }
/* * 多线程的实现方式1 * String getName() 返回该线程的名称。 * void setName(String name) 改变线程名称,使之与参数 name 相同。 */ public class ThreadDemo { public static void main(String[] args) { MyThread mt = new MyThread(); mt.setName("张三"); mt.start(); MyThread mt2 = new MyThread(); mt2.setName("李四"); mt2.start(); } }
二、实现Runnable接口
创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。
Runnable接口用来指定每个线程要执行的任务。包含了一个 run 的无参数抽象方法,需要由接口实现类重写该方法。
创建线程的步骤:
1、定义类实现Runnable接口。
2、覆盖接口中的run方法。
3、创建Thread类的对象。
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
5、调用Thread类的start方法开启线程。
public class MyThread2 implements Runnable { int num; public MyThread2(int num) { this.num = num; } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i + " - " + num); } } }
public class ThreadDemo2 { public static void main(String[] args) { //创建线程实例 MyThread2 mt = new MyThread2(100); Thread t = new Thread(mt); t.setName("张三"); t.start(); //创建线程实例 //Thread t2 = new Thread(mt); MyThread2 mt2 = new MyThread2(200); Thread t2 = new Thread(mt2); t2.setName("李四"); t2.start(); } }
多线程安全问题采用加锁解决。
线程状态转换
Java 语言定义了5种线程状态,在某一时刻,一个线程只能有其中的一种状态,这5种状态分别如下:
新建(New):创建后尚未启动的线程处于这种状态。
运行(Runable):Runable 包括了操作系统线程状态中的 Running 和 Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着 CPU 为它分配执行时间。
无限期等待(Waiting):处于这种状态的线程不会被分配 CPU 执行时间,它们要等待被其他线程显式地唤醒。 以下方法会让线程陷入无限期的等待状态:
- 没有设置 Timeout 参数的 Object.wait() 方法。
- 没有设置 Timeout 参数的 Thread.join() 方法。
- LockSupport.park() 方法。
限期等待(Timed Waiting):处于这种状态的线程也不会被分配 CPU 执行时间,不过无须等待被其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒。 以下方法会让线程进入限期等待状态:
- Thread.sleep() 方法。
- 设置了 Timeout 参数的 Object.wait() 方法。
- 设置了 Timeout 参数的 Thread.join() 方法。
- LockSupport.parkNanos() 方法。
- LockSupport.parkUntil() 方法。
阻塞(Blocked):线程被阻塞了,“阻塞状态”与“等待状态”的区别是:“阻塞状态”在等待着获取到一个排他锁,这个事件将在另外一个线程放弃这个锁的时候发生;而“等待状态”则是在等待一段时间,或者唤醒动作的发生。 在程序等待进入同步区域的时候,线程将进入这种状态。
结束(Terminated):已终止线程的线程状态,线程已经结束执行。上述5种状态在遇到特定事件发生的时候将会互相转换,它们的转换关系如下图。
注意:
- wait()方法会释放CPU执行权 和 占有的锁。
- sleep(long)方法仅释放CPU使用权,锁仍然占用;线程被放入超时等待队列,与yield相比,它会使线程较长时间得不到运行。
- yield()方法仅释放CPU执行权,锁仍然占用,线程会被放入就绪队列,会在短时间内再次执行。
- wait和notify必须配套使用,即必须使用同一把锁调用。
- wait和notify必须放在一个同步块中。
- 调用wait和notify的对象必须是他们所处同步块的锁对象。
Java 线程在JDK 1.2后,线程模型变为基于操作系统原生线程模型来实现。 操作系统支持怎样的线程模型,决定了Java虚拟机的线程是怎样映射。线程模型只对线程的并发规模和操作成本产生影响。
对于 Sun JDK 来说,它的 Windows 版与 Linux 版都是使用一对一的线程模型实现的,一条 Java 线程就映射到一条轻量级进程之中,因为 Windows 和 Linux 系统提供的线程模型就是一对一的。