① 格式
* 一个类继承 Thread 类并实现 run() ,调用 start() 启动线程。
② 常用方法
* getName()
获取当前线程名称
* currentThread()
获取当前线程
* sleep(long n)
使当前线程睡眠 n 毫秒
③ 示例
//创建一个类继承 Thread
public class MyThread extends Thread {
//重写 run() 方法
@Override
public void run() {
//创建线程任务
System.out.println("创建了一个线程");
}
}
//测试类
public class DemoThread {
public static void main(String[] args) {
//创建线程对象
MyThread thread = new MyThread();
//启动线程
thread.start();
//使用匿名内部类创建线程并启动
new Thread() {
@Override
public void run() {
System.out.println("创建了一个线程");
}
}.start();
}
}
2. 创建线程方式二
代码语言:javascript复制
① 格式
* 一个类实现 Runnable 接口并重写 run() 方法,在将实现类对象传到 Thread 中使用 start() 启动线程
② 使用 Runnable 的好处
* 避免的单继承的局限性
* 适合多个线程共享一个资源对象
③ 示例
//创建一个类实现 Runnable 接口并重写 run() 方法
public class RunnableImpl implements Runnable {
//重写 run() 方法
@Override
public void run() {
//创建线程任务
System.out.println("创建了一个线程");
}
}
//测试类
public class DemoRunnable {
public static void main(String[] args) {
//创建 Runnable 接口实现类对象
RunnableImpl impl = new RunnableImpl();
//使用 Thread 构造方法创建线程
Thread thread = new Thread(impl);
//启动线程
thread.start();
//使用匿名内部类创建线程
new Thread(new Runnable() {
//重写 run() 方法
@Override
public void run() {
for (int i = 20; i > 0; i--) {
System.out.println("创建了一个线程");
}
}
}).start();
}
}
三、线程安全
1. 线程不安全的原因
代码语言:javascript复制
① 多个线程访问同一个资源时
② 某一个线程在操作完资源,但是还未进行记录时,失去了 CPU 执行权
③ 另一个线程获得了 CPU 的执行权,对资源进行操作,这就导致了线程不安全
2. 同步代码块(synchronized)
代码语言:javascript复制
① 格式
synchronized (锁对象) {
//同步内容
}
② 注意
* 锁对象可以是任意对象,但是多个线程使用的锁对象必须是同一个。
* 一个线程获取锁对象进入同步代码块时,其他进程就算获取了 CPU 执行权也无法进入同步代码块,
只有等锁对象释放以后,获取到锁对象后才能访问。
③ 示例
public class DemoTicket {
public static void main(String[] args) {
//使用匿名内部内创建Runnable对象
Runnable r = new Runnable() {
//总票数
int num = 100;
@Override
public void run() {
//使循环一直开启
while (true) {
//同步代码块
/*
锁可以使用 对象名、this还可以使用该类的字节码文件
*/
synchronized (DemoTicket.class) {
//判断是否有余票
if (num > 0) {
try {
//模拟卖票时间
Thread.sleep(0);
//模拟出票
System.out.println(Thread.currentThread().getName() "正在卖第" num "张票");
//票数减1
num--;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
};
//开启线程
new Thread(r, "1号窗口").start();
new Thread(r, "2号窗口").start();
new Thread(r, "3号窗口").start();
}
}
3. 同步方法
代码语言:javascript复制
① 格式
修饰符 synchronized 返回值 方法名(参数列表) {
//方法体
}
② 示例
public class DemoTicket {
public static void main(String[] args) {
//使用匿名内部内创建Runnable对象
Runnable r = new Runnable() {
//总票数
int num = 100;
@Override
public void run() {
//使循环一直开启
while (true) {
ticket(num);
}
}
};
//开启线程
new Thread(r, "1号窗口").start();
new Thread(r, "2号窗口").start();
new Thread(r, "3号窗口").start();
}
}
public static synchronized void ticket(int num) {
//判断是否有余票
if (num > 0) {
try {
//模拟卖票时间
Thread.sleep(0);
//模拟出票
System.out.println(Thread.currentThread().getName() "正在卖第" num "张票");
//票数减1
num--;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4. Lock锁
代码语言:javascript复制
① 格式
Lock lock = new ReentrantLock();
lock.lock();
lock.unlock();
② 示例
import java.util.concurrent.locks.ReentrantLock;
public class DemoTicket {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
//使用匿名内部内创建Runnable对象
Runnable r = new Runnable() {
//总票数
int num = 100;
@Override
public void run() {
//使循环一直开启
while (true) {
//同步代码块
lock.lock();
//判断是否有余票
if (num > 0) {
try {
//模拟卖票时间
Thread.sleep(0);
//模拟出票
System.out.println(Thread.currentThread().getName() "正在卖第" num "张票");
//票数减1
num--;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.unlock();
}
}
};
//开启线程
new Thread(r, "1号窗口").start();
new Thread(r, "2号窗口").start();
new Thread(r, "3号窗口").start();
}
}
四、线程状态
1. 七种线程状态
代码语言:javascript复制
① new(新建):线程刚刚被创建,但是并未启动
② Runnable(可运行):线程可以运行但是没有 CPU 执行权
③ Running(运行):线程正在执行过程中
④ Blocked(锁阻塞):线程无法获取到锁对象
⑤ Waiting(无限等待):一个线程在等待另一个线程的唤醒
⑥ Time Waiting(计时等待):线程等待 n 毫秒后自动唤醒
⑦ Teminated(被终止):线程执行结束
2. 等待唤醒机制
代码语言:javascript复制
① 常用方法
* wait()
等待另一个线程唤醒
* wait(long l)
线程等待 l 毫秒后自动被唤醒
* notify()
唤醒在等待中的一个线程,等待最久的有限被唤醒
* notifyAll()
唤醒所有等待的线程
② 注意
* wait() 需要在同步代码块或者同步方法中使用
* wait() 和 notify() 需要使用同一个锁对象(定义在 Object 中,任何一个类都可以使用)
* wait() 会释放锁而 sleep() 不会释放锁
③ 示例
//定义一个标准类
public class Commodity {
private String type;
private Boolean state;
//省略 构造方法 set/get
}
public class Demo {
public static void main(String[] args) {
//创建锁对象
Object obj = new Object();
//创建商品对象
Commodity type = new Commodity("烤鸡",false);
//创建厨子线程
Runnable cook = new Runnable() {
@Override
public void run() {
while (true) {
synchronized (obj) {
//判断是否有货
if (type.getState()) {
try {
//有货则等待
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
System.out.println(Thread.currentThread().getName() "做" type.getType());
//没有或则生产货
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
//更行货物状态
type.setState(true);
//唤醒顾客线程吃
obj.notifyAll();
}
}
}
}
};
//创建顾客线程
Runnable customer = new Runnable() {
@Override
public void run() {
while (true) {
synchronized (obj) {
//判断是否有货
if (type.getState()) {
try {
//有货则吃
System.out.println(Thread.currentThread().getName() "吃" type.getType());
Thread.sleep(0);
//更新货物状态
type.setState(false);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
//没货则唤醒厨子线程做
obj.notify();
//等待厨子做
obj.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
};
//启动线程
new Thread(cook,"厨子").start();
new Thread(customer,"顾客1:").start();
new Thread(customer,"顾客2:").start();
}
}
五、线程池
1. 线程池思想
代码语言:javascript复制
① 什么是线程池
* 将创建好的一堆线程扔到一个容器内,想用的时候拿出来,用完了放回去。
② 线程池的好处
* 降低了资源的消耗,每个线程都重复利用
* 提高了相应速度,不需要每次都向 CPU 要求开辟通道
* 便于管理
2. 线程池的使用
代码语言:javascript复制
① 格式
ExecutorService threadPool = Executors.newFixedThread(int nThreads);
threadPool.submit(Runnable r);
② 常用方法
* shutdown()
不允许在提交新的线程,已提交的线程执行结束后关闭线程池
* shutdownNow()
立即停止所有线程
* submit(Callable<T> task)
提交一个线程,返回一个处理结果
③ 示例
public class Commodity {
private String type;
private Boolean state;
//省略 构造方法 set/get
}
public class Demo {
public static void main(String[] args) {
//创建锁对象
Object obj = new Object();
//创建商品对象
Commodity type = new Commodity("烤鸡",false);
//创建厨子线程
Runnable cook = new Runnable() {
@Override
public void run() {
while (true) {
synchronized (obj) {
//判断是否有货
if (type.getState()) {
try {
//有货则等待
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
System.out.println(Thread.currentThread().getName() "做" type.getType());
//没有或则生产货
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
//更行货物状态
type.setState(true);
//唤醒顾客线程吃
obj.notifyAll();
}
}
}
}
};
//创建顾客线程
Runnable customer = new Runnable() {
@Override
public void run() {
while (true) {
synchronized (obj) {
//判断是否有货
if (type.getState()) {
try {
//有货则吃
System.out.println(Thread.currentThread().getName() "吃" type.getType());
Thread.sleep(0);
//更新货物状态
type.setState(false);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
//没货则唤醒厨子线程做
obj.notify();
//等待厨子做
obj.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
};
//创建线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
//提交线程任务
threadPool.submit(cook);
threadPool.submit(customer);
threadPool.submit(customer);
}
}