1.1 程序,进程,线程
- 程序:完成一定功能的静态代码,对比火车站售票厅
- 进程:正在执行的静态代码,称为进程,对比北京西站售票厅
- 线程:并发执行一些异步任务,比对售票的中多个窗口
- 单核cpu和多核cpu
- java程启动后,main线程,gc线程,异常处理线程
1.2 并行和并发
- 并行:多核cpu下,多线程同时执行
- 并发:如果是单核cpu,采用抢占时cpu调度模型,让cpu在多个线程之间切换执行
完成一个任务需要多少个线程
- io密集型 cpu核数*2
- cpu密集型 cpu核数
- 混合型 经验
1.3 线程使用的场景
- 网络连接tomcat,mysql,一个连接对一个一个线程, one connection one thread
- 文件操作,文件下载,后台启动一个线程异步执行长时间的任务
2 多线程实战
2.1 Thread方式实现多线程
- 定义子类继承Thread类。
- 类中重写Thread类中的run方法。
- 创建Thread子类对象,即创建了线程对象。
- 调用线程对象start方法:启动线程,调用run方法。
启动一个线程,在线程中执行1-10000的偶数打印工作
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <= 10000; i++) {
if (i % 2 == 0) {
//Thread.currentThread().getName():得到线程的名字
System.out.println(Thread.currentThread().getName() + "\t" + i);}
}
}
}
测试:
public class Test1 {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
myThread1.start();MyThread myThread2 = new MyThread();
myThread2.start();System.out.println(Thread.currentThread().getName() + " main 线程 over");
}
}
//如果子线程执行,进程不会停止:
//弹窗挂起
JOptionPane.showMessageDialog(null, "是否确认向下执行...."); //主线程进入IO阻塞
System.out.println("main over");
2.2 Runable方式实现多线程
- 定义子类,实现Runnable接口。
- 类中重写Runnable接口中的run方法。
- 通过Thread类含参构造器创建线程对象。
- 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
- 调用Thread类的start方法:开启线程, 调用Runnable子类接口的run方法。
public class ThreadDemo {
public static void main(String[] args) {
//方式1:Thread子类,启动
MyThread thread1 = new MyThread();
thread1.start();//方式2:Runable方式(推荐的方式)
//优势:可以实现多继承,比如继承BaseDao,然后再实现Runnable
MyTask task = new MyTask();
Thread thread2 = new Thread(task);
thread2.start();//方式3:匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}).start();
}}
//方式1
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}//方式2
//优势:可以实现多继承,比如继承BaseDao,然后再实现Runnable
class MyTask implements Runnable {@Override
public void run() {
for (int i = 0; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
2.3 Thread常见方法
- 构造函数:
-
- Thread(): 创建新的Thread对象
- Thread(String threadname): 创建线程并指定线程实例名
- Thread(Runnable target): 指定创建线程的目标对象,它实现了Runnable接口中的run方法
- Thread(Runnable target, String name): 创建新的Thread对象
- void start(): 启动线程,并执行对象的run()方法
- run(): 线程在被调度时执行的操作
- String getName(): 返回线程的名称
- void setName(String name):设置该线程名称
- static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
- static void yield(): 线程让步
-
- 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
- 若队列中没有同优先级的线程,忽略此方法
- join() : 当某个程序执行流中调用其他线程的 join() 方法时, 调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止
- static void sleep(long millis): (指定时间:毫秒) 令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。
- stop(): 强制线程生命期结束,不推荐使用
- boolean isAlive(): 返回boolean,判断线程是否还活着
2.3 sleep
指定线程休眠的时间,单位毫秒,让出cpu时间片,其他线程可以抢占cpu时间片
public class MyTask implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
//Thread.currentThread().getName():得到线程的名字
System.out.println(Thread.currentThread().getName() + "\t" + i);
try {
Thread.sleep(1000);
Thread.yield();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}}
}
2.4 线程优先级
- 线程的优先级等级
-
- MAX_PRIORITY: 10
- MIN _PRIORITY: 1
- NORM_PRIORITY: 5
public static void main(String[] args) {
MyTask myTask = new MyTask();
Thread thread1 = new Thread(myTask, "t1");
thread1.setPriority(Thread.MIN_PRIORITY);
thread1.start();Thread thread2 = new Thread(myTask, "t2");
thread1.setPriority(Thread.MAX_PRIORITY);
thread2.start();
}
2.5 守护线程
- 其他线程都执行结束,守护线程自动结束
- 守护启动子线程,也是守护线程
- 守护线程的语法
thread.(setDaemon(true)
设置守护线程
public class Test2 {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
// myThread1.setDaemon(true);
myThread1.start(); //守护线程, gc线程,jvm线程结束gc会自动结束
JOptionPane.showMessageDialog(null, "是否确认向下执行...."); //主线程进入IO阻塞
System.out.println("main over");
}}
class MyThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println("a");
}
}
}
2.6 线程合并join
public class Test1 {
/**
* CountDownLatch:可以实现相同的效果
* @param args
*/
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
t1.start();try {
t1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}System.out.println(Thread.currentThread().getName() + " main orver");
}
}
结果:
2.7 线程退出
2.7.1 stop
不推荐,线程退出方式粗暴,不管线程正在执行的任务,直接退出,可能丢失数据
public class Test1 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyTask());
t1.start();Scanner in = new Scanner(System.in);
System.out.println("输入1/0:0表示退出");
int i = in.nextInt(); ///主线程进入IO阻塞if (i == 0) {
t1.stop();
}System.out.println("main over");
}
static class MyTask implements Runnable {@Override
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}}
2.7.2 中断信号interrupt
- interrupt():发送中断信号(true)
-
- 如果线程在阻塞状态,比如sleep(),join(),wait(),这时接收到中断信号会抛出一个异常InterruptException,同时中断信号清除(false)
- 只是发送信号,不会对线程产生影响
- static interrupted():得到中断信号(true),然后把中断信号设置成false
- isInterrupted():得到中断信号,不会清除中断信号
public class Test1 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyTask());
t1.start();JOptionPane.showMessageDialog(null, "是否确认向下执行...."); //主线程进入IO阻塞
t1.interrupt(); //发送中断信号给t1System.out.println("main over");
}static class MyTask implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("a");try {
Thread.sleep(500000);
} catch (InterruptedException e) {
System.out.println("bbbbbbbbbbbbbbb");
e.printStackTrace();
Thread.currentThread().interrupt(); //再次发送中断信号,中断信号发给阻塞线程,抛出Interrupt异常,中断信号清除
// throw new RuntimeException(e);
}//得到中断信号,优雅的退出
if (Thread.interrupted()) {
break;
}}
}
}}