API文档 => java.lang => Thread,Runnable
线程的概述
创建线程的方式
- 创建一个Thread类,或者一个Thread子类的对象
- 创建一个实现Runnable接口的类的对象
Thread类
是一个线程类,位于java.lang包下

Runnable接口
- 只有一个方法run()
- Runnable是Java中用以实现线程的接口
- 任何实现线程的类都必须实现该接口
线程的创建
通过Thread类创建线程
案例1:简单的案例
- 这里有两个线程,主线程和副线程
- 副线程运行的时间是随机的,因为它什么时候获得CPU使用权不一定
- 线程不能多次启动,也就是说不能多次调用start方法 ```cpp package com.thread;
class MyThread extends Thread{ public void run(){ System.out.println(getName()+”该线程正在执行!”); } }
public class ThreadTest { public static void main(String[] args){ System.out.println(“主线程1”); MyThread myThread = new MyThread(); myThread.start(); System.out.println(“主线程2”); } }
主线程1<br />主线程2<br />Thread-0该线程正在执行!案例2:复杂的案例<br />可以明显看出,线程运行的随机性```cpppackage com.thread;class MyThread extends Thread{public MyThread(String name){super(name);}public void run(){for (int i=0;i<10;i++){System.out.println(getName()+"正在执行"+i);}}}public class ThreadTest {public static void main(String[] args){MyThread myThread1 = new MyThread("线程1");MyThread myThread2 = new MyThread("线程2");myThread1.start();myThread2.start();}}
执行结果可能是这样:
主线程1
线程1正在执行0
线程1正在执行1
线程1正在执行2
线程1正在执行3
线程1正在执行4
主线程2
线程2正在执行0
线程2正在执行1
线程2正在执行2
线程2正在执行3
线程2正在执行4
执行结果也可能是这样:
主线程1
线程1正在执行0
线程1正在执行1
线程1正在执行2
线程1正在执行3
线程1正在执行4
线程2正在执行0
线程2正在执行1
线程2正在执行2
线程2正在执行3
线程2正在执行4
主线程2
实现Runnable接口创建线程
已经有通过Thread类创建线程的方式,为什么要还需要此方式呢?
- Java不支持多继承
如果一个类已经继承了某个父类,那么就无法通过继承Thread类的方式创建线程
- 不打算重写Thread类的其他方法
一般来说,重写run方法就够我们用了,不打算重写其他方法的话,还是实现Runnable接口的方式比较方便
案例1:简单的例子
package com.runnable;class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "正在运行!");}}public class RunnableTest {public static void main(String[] args){// 1. 定义Runnable类的对象MyRunnable myRunnable = new MyRunnable();// 2. 使用Runnable类的对象,创建线程类的对象Thread thread = new Thread(myRunnable,"主线程");// 3. 启动线程thread.start();}}
运行结果:主线程正在运行!
案例2-1:复杂的例子
两个线程都运行5次
package com.runnable;class MyRunnable implements Runnable{@Overridepublic void run() {int i=0;while (i<5){System.out.println(Thread.currentThread().getName() + "正在运行" + i++);}}}public class RunnableTest {public static void main(String[] args){MyRunnable myRunnable1 = new MyRunnable();MyRunnable myRunnable2 = new MyRunnable();Thread thread1 = new Thread(myRunnable1);thread1.start();Thread thread2 = new Thread(myRunnable2);thread2.start();}}
Thread-0正在运行0
Thread-0正在运行1
Thread-0正在运行2
Thread-0正在运行3
Thread-0正在运行4
Thread-1正在运行0
Thread-1正在运行1
Thread-1正在运行2
Thread-1正在运行3
Thread-1正在运行4
案例2-2:复杂的例子
运行结果与案例2-1是一致的,两个线程都运行5次
package com.runnable;class MyRunnable implements Runnable{@Overridepublic void run() {int i=0;while (i<5){System.out.println(Thread.currentThread().getName() + "正在运行" + i++);}}}public class RunnableTest {public static void main(String[] args){MyRunnable myRunnable = new MyRunnable();Thread thread1 = new Thread(myRunnable);thread1.start();Thread thread2 = new Thread(myRunnable);thread2.start();}}
Thread-0正在运行0
Thread-0正在运行1
Thread-0正在运行2
Thread-0正在运行3
Thread-0正在运行4
Thread-1正在运行0
Thread-1正在运行1
Thread-1正在运行2
Thread-1正在运行3
Thread-1正在运行4
案例2-3:复杂的例子
运行结果与案例2-1,2-2是一致的,两个线程都运行5次
package com.runnable;class MyRunnable implements Runnable{int i = 0;@Overridepublic void run() {while (i<5){System.out.println(Thread.currentThread().getName() + "正在运行" + i++);}}}public class RunnableTest {public static void main(String[] args){MyRunnable myRunnable1 = new MyRunnable();MyRunnable myRunnable2 = new MyRunnable();Thread thread1 = new Thread(myRunnable1);thread1.start();Thread thread2 = new Thread(myRunnable2);thread2.start();}}
Thread-0正在运行0
Thread-0正在运行1
Thread-0正在运行2
Thread-0正在运行3
Thread-0正在运行4
Thread-1正在运行0
Thread-1正在运行1
Thread-1正在运行2
Thread-1正在运行3
Thread-1正在运行4
案例2-4:复杂的例子
运行结果与案例2-1,2-2,2-3不一致,两个线程加起来运行5次
线程thread1和线程thread2共享同一个资源myRunnable,所以加起来运行5次之后就结束了。
也就是说,多个线程可以处理同一个资源
package com.runnable;class MyRunnable implements Runnable{int i = 0;@Overridepublic void run() {while (i<5){System.out.println(Thread.currentThread().getName() + "正在运行" + i++);}}}public class RunnableTest {public static void main(String[] args){MyRunnable myRunnable = new MyRunnable();Thread thread1 = new Thread(myRunnable);thread1.start();Thread thread2 = new Thread(myRunnable);thread2.start();}}
Thread-0正在运行1
Thread-0正在运行2
Thread-0正在运行3
Thread-0正在运行4
Thread-1正在运行0
线程的状态和生命周期
获取cpu使用权的时间是不一定的
#Java已经不建议使用stop方法

sleep方法的使用
- Thread类的方法 pubic static void sleep(long millis)
- 作用是在指定毫秒数内让正在执行的线程休眠(暂停执行)
- 参数为休眠的时间,单位是毫秒
package com.sleep;class MyThread implements Runnable{@Overridepublic void run() {for (int i=1;i<10;i++){System.out.println(Thread.currentThread().getName()+"执行第"+i+"次");try{Thread.sleep(1000);}catch (InterruptedException e){e.printStackTrace();}}}}public class SleepDemo {public static void main(String[] args){MyThread myThread = new MyThread();Thread thread = new Thread(myThread);thread.start();}}
大概每隔1秒输出一次
为什么说是大概每隔1秒呢?Thread.sleep(1000);的意思是暂停线程1秒,暂停之后线程状态会切换到可运行(Runnable)状态,而在可运行状态下获取cpu使用权的时机是不一定的,也许是马上,可有可能是30ms之后,所以这里说大概每隔1秒。
join方法的使用
- Thread类的方法 pubic final void join(long millis)
- 作用是等待调用该方法的线程结束后主线程才能执行,也就是说,可以使主他线程由正在运行状态变成阻塞状态
- 参数为抢占时间
如果不写参数,那么调用该方法的线程结束后执行其他线程
如果写了参数,那么该时间结束后执行其他线程
package com.thread;class MyThread extends Thread{public void run(){for (int i=0;i<5;i++){System.out.println(getName()+"正在执行"+i);}}}public class ThreadTest {public static void main(String[] args){MyThread myThread = new MyThread();myThread.start();try {myThread.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("主线程运行结束");}}
Thread-0正在执行0
Thread-0正在执行1
Thread-0正在执行2
Thread-0正在执行3
Thread-0正在执行4
主线程运行结束
重点:可以看到,一定是Thread-0线程结束之后,才会执行主线程的
线程的优先级
- Java为线程类提供了10个优先级
- 优先级可以用整数1-10表示,超过范围会抛出异常
- 主线程默认优先级为5
- 优先级常量
MAX_PRIORITY:线程的最高优先级10
MIN_PRIORITY:线程的最低优先级1
NORMAL_PRIORITY:线程的默认优先级5
- 优先级相关的方法

package com.thread;class MyThread extends Thread{private String name;public MyThread(String name){this.name = name;}public void run(){for (int i=0;i<5;i++){System.out.println("线程" + name + "正在执行"+i);}}}public class ThreadTest {public static void main(String[] args){// 获取主线程的优先级int mainPriority = Thread.currentThread().getPriority();System.out.println("主线程的优先级为" + mainPriority);MyThread myThread1 = new MyThread("线程1");MyThread myThread2 = new MyThread("线程2");myThread1.setPriority(Thread.MAX_PRIORITY); // 跟设置为10是一样的myThread2.setPriority(Thread.MIN_PRIORITY); // 跟设置为1是一样的myThread2.start();myThread1.start();System.out.println("线程1的优先级为:" + myThread1.getPriority());System.out.println("线程2的优先级为:" + myThread2.getPriority());}}
主线程的优先级为5
线程线程2正在执行0
线程线程2正在执行1
线程线程2正在执行2
线程线程2正在执行3
线程线程2正在执行4
线程1的优先级为:10
线程2的优先级为:1
线程线程1正在执行0
线程线程1正在执行1
线程线程1正在执行2
线程线程1正在执行3
线程线程1正在执行4
重点:虽然线程1的优先级比较高,但是并不是线程1线运行,这个CPU,操作系统都有关系。不过,虽然执行结果可能是随机的,但是大大提高了优先级高的线程优先执行的概率。
线程同步
普通线程(非同步)的特点
- 各个线程是通过竞争CPU时间而获得运行机会的
- 各线程什么时候得到CPU时间,占用多久,是不可预测的
- 一个正在运行着的线程在什么地方暂停是不确定的
普通线程(非同步)因为具有以上特点,所以当两个线程操作一个共享对象的时候,可能会出现问题。
让我们通过一个银行的存取款例子,来说明这种可能发生的问题。
问题描述
Bank.java
package com.process;public class Bank {private String account;private int balance;public Bank(String account, int balance) {this.account = account;this.balance = balance;}public String getAccount() {return account;}public void setAccount(String account) {this.account = account;}public int getBalance() {return balance;}public void setBalance(int balance) {this.balance = balance;}@Overridepublic String toString(){return "账号:"+account+",余额:"+balance;}// 存款public void saveAccount(){int balance = getBalance();balance += 100;setBalance(balance);System.out.println("存款后的余额为:"+balance);}// 取款public void drawAccount(){int balance = getBalance();balance -= 200;// 通过sleep方法模拟被其他线程中断的情况try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}setBalance(balance);System.out.println("取款后的余额为:"+balance);}}
SaveAccount.java
package com.process;public class SaveAccount implements Runnable{Bank bank;public SaveAccount(Bank bank){this.bank = bank;}@Overridepublic void run() {bank.saveAccount();}}
DrawAccount.java
package com.process;public class DrawAccount implements Runnable {Bank bank;public DrawAccount(Bank bank) {this.bank = bank;}@Overridepublic void run() {bank.drawAccount();}}
Test.java
package com.process;public class Test {public static void main(String[] args){// 创建账户,给定余额为1000Bank bank = new Bank("song",1000);// 创建线程对象SaveAccount saveAccount = new SaveAccount(bank);DrawAccount drawAccount = new DrawAccount(bank);Thread save = new Thread(saveAccount);Thread draw = new Thread(drawAccount);draw.start();save.start();try {draw.join();save.join();} catch (InterruptedException e) {e.printStackTrace();}// 以上的join()是为了让这个输出语句最后执行System.out.println(bank);}}
join()方法在主方法中,只能保证调用join()方法的线程执行完才能执行主线程,但是影响不了主方法中的另一个线程。也就是说,draw.join()方法可以保证draw()方法执行完后,再执行主线程,但是不影响save线程的执行。
运行结果如下,这显然是不对的,银行系统出现这种问题,后果将是灾难性的。
存款后的余额为:1100
取款后的余额为:800
账号:song,余额:800
我们来分析下为什么会发生这种情况,主要原因是因为线程中断而没有及时更新银行对象的余额。
- 先取款,取款线程中发生的事情是这样的:
函数的balance值更新为800,被中断
- 再存款,存款线程中发生的事情是这样的
取得银行对象的balance值为1000(因为取款线程被中断了,所以余额还未更新)
函数的balance值更新为1100
银行对象的balance值更新为1100
输出,即“存款后的余额为:1100”
- 回到取款线程
银行对象的balance值更新为800
输出,即“取款后的余额为:800”
- 回到主程序,输出“账号:song,余额:800”
那该怎么防止这种情况的发生呢?这时候就需要用到线程同步了
保证在存款或取款的时候,不允许其他线程对账户余额进行操作
需要将Bank对象进行锁定
使用同步关键字synchronized,实现共享对象在同一时刻只能被一个线程访问
synchronized关键字
成员方法 public synchronized void saveAccount(){}
静态方法 public static synchronized void saveAccount(){}
语句块 synchronized(obj){ … }
解决方案
package com.process;public class Bank {private String account;private int balance;。。。无修改,省略。。。// 存款public synchronized void saveAccount(){int balance = getBalance();balance += 100;setBalance(balance);System.out.println("存款后的余额为:"+balance);}// 取款public void drawAccount(){synchronized (this){int balance = getBalance();balance -= 200;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}setBalance(balance);System.out.println("取款后的余额为:"+balance);}}}
运行结果如下:
取款后的余额为:800
存款后的余额为:900
账号:song,余额:900
或者
存款后的余额为:1100
取款后的余额为:900
账号:song,余额:900
线程间通信
wait()方法:中断方法的执行,使线程等待
notify()方法:唤醒处于等待的某一个线程,使其结束等待
notifyAll()方法:唤醒处于等待的所有线程,是它们结束等待
Queue.java
package com.queue;public class Queue {private int n;boolean isActive = false;public synchronized int getN() {if(!isActive){try {wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("消费:"+n);isActive = false;notifyAll(); // 避免死锁问题return n;}public synchronized void setN(int n) {if (isActive){try {wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("生产:"+n);this.n = n;isActive = true;notifyAll(); // 避免死锁问题}}
Consumer.java
package com.queue;public class Consumer implements Runnable {Queue queue;public Consumer(Queue queue) {this.queue = queue;}@Overridepublic void run() {while (true){queue.getN();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}
Producer.java
package com.queue;public class Producer implements Runnable{Queue queue;public Producer(Queue queue) {this.queue = queue;}@Overridepublic void run() {int i=0;while (true){queue.setN(i++);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}
Test.java
package com.queue;public class Test {public static void main(String[] args){Queue queue = new Queue();new Thread(new Producer(queue)).start();new Thread(new Consumer(queue)).start();}}
