javaSE_day13
线程
1. 程序、进程、线程
- 程序:静态的代码
- 进程:程序动态的执行过程。产生、执行、消亡
- 线程:动态执行的过程,是进程内最小的执行单位
进程有直接独立的内存
线程共享进程的资源,在进程内,每个线程可以完成独立的功能
eg: 线程之间实际上轮换执行
package cn.tedu.thread;
class T extends Thread{
//线程完成的功能代码要写到线程体方法中,run()
@Override
public void run() {
for (int i = 0; i <100; i++) {
System.out.println("hello");
}
}
}
class T2 extends Thread{
//线程完成的功能代码要写到线程体方法中,run()
@Override
public void run() {
for (int i = 0; i <100; i++) {
System.out.println("Thread");
}
}
}
public class MyThread {
//主线程
public static void main(String[] args) {
T t1 = new T();
T2 t2 = new T2();
t1.start();
t2.start();
}
}
2. 线程创建的两种方式
- 继承Thread类 —— 是线程类
- 实现Runnable接口 —— 不是线程类
代码:要写到run()方法里
启动线程:start()方法
练习:用两种方法创建两个线程,一个打印10以内的偶数,一个打印10以内的奇数。
3. 线程类的构造方法
- Thread()
- Thread(String name)
- Thread(Runnable r)
- 功能:实现接口类的run方法
- Thread(Runnable r,String name)
4. 常用方法
- 返回正在运行的线程对象
Thread t = Thread.currentThread(); - 返回线程的名称
t.getName(); - 返回线程的唯一id
t.getId(); - 判断线程是否可用
t.isAlive(); - sleep方法:线程休眠
Thread.sleep() - join方法:等待线程执行完毕
t.join()
- 练习
- 模拟倒计时
package cn.tedu.thread;
/**
* 模拟倒计时
* @author Dell
*
*/
public class Demo6 {
public static void main(String[] args) {
new Thread(
()->{
System.out.println(Thread.currentThread().getName());
System.out.println("倒计时开始");
for (int i = 10; i >0; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("过年啦");
},"线程sleep") .start();
}
}
- 模拟图片显示和图片下载
package cn.tedu.thread;
/**
* 练习:模拟图片显示和图片下载
* @author Dell
*/
public class Demo8 {
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
System.out.println("Thread-1:开始下载图片");
for (int i = 1; i <=10; i++) {
System.out.println("Thread-1:下载进度"+i+"%");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("Thread-1:下载图片完成");
}
};
Thread t2 = new Thread(
()->{
System.out.println("Thread-2:显示图片");
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread-2:显示图片完成");
});
//通常资源够用的情况下,将先执行的线程启动放在前面,则会先执行前面的线程
t2.start();
t1.start();
}
}
5. 守护线程 setDeamon(true)
- 创建出来的线程:用户线程(前台线程)和守护线程(后台线程)。
- 一般创建出来的线程都是用户线程。
- 设置守护线程:调用setDeamon(true);
- 用法和创建线程方式没有区别
- 区别:用户线程执行完毕后,守护线程无条件停止执行。
eg:
6. 线程的生命周期(从生到死)
- 新建状态:new Thread()
- 就绪状态:start()
- 阻塞状态:sleep()、wait()、io
- 解除阻塞状态后重新处于就绪状态
- 运行状态:run() 执行方法(自动执行)
- 消亡:执行完run()
7. 线程的优先级
- 不同优先级:高优先级先执行,低优先级后执行 (高优先级不一定最先执行,只是拥有优先权)
- 同一优先级:先到先服务
- 线程优先级的规范:
- 系统默认为中等优先级(5)
- 人为设置线程的优先级:
8. 线程同步(synchronized)
- 多个线程操作一个共享变量时,可能会导致共享变量的不完整(不安全)。
- 为了保证共享变量的安全,采用同步机制保证共享变量的安全性。
- 同步机制:当一个线程改变共享变量值的时候,其他线程不能使用共享变量,当线程计算完成,返回变量值之后,其他线程才可以使用共享变量。
使用synchronized关键字实现线程同步的两种方法:
- synchronized同步块
Synchronized(锁对象){
操纵共享变量的代码(共享变量相关的业务代码(运算))
}
锁对象只能有一个,否则可能出现死锁现象。 - synchronized修饰方法,用此关键字修饰的方法叫同步方法
- 练习:
- 商场买衣服:商场挑衣服(异步),试衣间只有一个,试衣服是同步。
package cn.tedu.thread;
/**
* 练习
* synchronized关键字实现线程同步
* @author Dell
*/
class Shopping {
public void test() {
System.out.println(Thread.currentThread().getName()+"挑选衣服");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (this) {
System.out.println(Thread.currentThread().getName()+"试衣服......");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Demo12 {
public static void main(String[] args) {
Shopping s = new Shopping();
Thread t = new Thread(()->{
s.test();
}) ;
Thread t2 = new Thread(()->{
s.test();
}) ;
t.start();
t2.start();
}
}
- 一个账号,两个人存钱
package cn.tedu.thread;
/**
* 模拟银行存钱
* @author Dell
*
*/
class Bank{
private int money=100;
public synchronized void save(int money) {
for (int i = 1; i <=3; i++) {
this.money += money;
System.out.println(Thread.currentThread().getName()+"余额:"+this.money);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Demo13 {
public static void main(String[] args) {
Bank b1 = new Bank();
Thread t1 = new Thread(() ->{
b1.save(1000);
});
Thread t2 = new Thread(() ->{
b1.save(1000);
});
t1.start();
t2.start();
}
}
9. 同步线程之间的通信
- wait():使当前线程处于等待状态,释放锁
- notify():唤醒wait的线程
- notifyAll():唤醒所有wait的线程
使用时要用锁对象调用这三个方法,否则会报异常
sleep和wait方法的区别:
sleep | wait | |
---|---|---|
1. | 是线程类的方法 | 是Object类的方法 |
2. | 没有释放锁 | 释放锁 |
3. | 超时或者调用interrupt()方法体 | 必须使用notify,notifyAll唤醒 |
4. | 可以在任何地方使用 | wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用 |
5. | 静态方法 | 实例方法 |
eg: 使用两个线程交替打印10。
练习:生产者生产产品和消费者取走产品
10. 线程的死锁
eg:
package cn.tedu.thread;
/**
* 线程死锁
* @author Dell
*/
public class Demo {
public static void main(String[] args) {
StringBuffer str1 = new StringBuffer();
StringBuffer str2 = new StringBuffer();
synchronized (str1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (str2) {
System.out.println(str1);
System.out.println(str2);
}
}
synchronized (str2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (str1) {
System.out.println(str1);
System.out.println(str2);
}
}
}
}
11. 线程池
-
为什么使用线程池?
频繁的创建线程,需要耗费时间和内存 -
使用线程池的好处:
管理线程、使线程重用 -
创建线程池的四种方法:
(1) newCachedThreadPool
(2) newFixedThreadPool可以指定线程池中的线程数。- shutdown()不关闭正在执行的线程,要等到全部线程执行完
- shutdownNow()不管线程是否执行完成,立即停止执行
(3) newScheduleThreadPool 定时周期性执行功能
(new Runnable(),1,5,TimeUnit,SECONDS) 延迟1秒启动,间隔5秒启动下一个
(4) newSingleThreadExecutor 单个线程