-
IDEA的使用
-
程序进程线程的概念
-
单核与多核CPU的任务执行:并行与并发
-
多线程的优点
-
创建多线程的方式一:继承Thread
-
线程常用方法
-
线程优先级的设置
-
例题:多窗口卖票(继承Thread方式)
-
创建多线程的方式二:实现Runnable接口
-
例题:多窗口卖票(实现Runnable接口方式)
-
两种创建方式的对比
1,IDEA的使用
2,程序进程线程的概念
3,单核与多核CPU的任务执行:并行与并发
4,多线程的优点
需要创建多线程的情景: 1,程序需要同时执行多个任务时 2,程序需要实现一些等待的任务时,比如用户输入、文件读写操作、网络操作、搜索等 3,需要一些后台运行的程序时 一个程序的执行过程如果是“一条线”,那它就是单线程的方式,如果可以用“多条线”表示,那它就可以用多线程的方式执行
5,创建多线程的方式一:继承Thread
package com.atguigu.java;
/**
* 继承Thread类创建多线程的过程
* 1,创建一个继承Thread类的子类
* 2,重写Thread类的run()
* 3,创建子类的对象
* 4,对象调用start():1,启动一个线程。2,调用这个线程类中重写过的run()
*/
public class ThreadTest {
public static void main(String[] args) { // main()是一条线程
MyThread t1 = new MyThread();
// t1.run(); // 在main()这条线程中执行,并没有开启新的线程
t1.start(); // 调用Thread中的start(),开启了另一条线程,这些都是main()这条线程做的
// t1.start(); // 想通过t1再创建一个新的线程,会报IllegalThreadStateException的错误
// MyThread t2 = new MyThread(); // 需要创建一个新的对象来调用start(),创建新的线程
// t2.start();
System.out.println("hello"); // 在第一条线程中执行,两条线程是并行的,hello可能输出在
// 数字的前面、中间、后面
}
}
class MyThread extends Thread {
public void run() { // 希望创建的这个线程要做什么事,写在run()中。在第二条线程中执行
for(int i = 0 ; i < 100 ; i++) {
if(i % 2 == 0) {
System.out.println(i);
}
}
}
}
练习
package com.atguigu.exer;
public class ThreadDemo {
public static void main(String[] args) {
// MyThread1 m1 = new MyThread1();
// MyThread2 m2 = new MyThread2();
//
// m1.start();
// m2.start();
new Thread() { // 通过创建Thread类的匿名子类的匿名对象来调用start(),start()调用的run()是重写过的run()
@Override
public void run() {
for(int i = 0 ; i < 100 ; i++) {
if(i % 2 == 0)
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}.start();
new Thread() {
@Override
public void run() {
for(int i = 0 ; i < 100 ; i++) {
if(i % 2 != 0)
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}.start();
}
}
class MyThread1 extends Thread {
public void run() {
for(int i = 0 ; i < 100 ; i++) {
if(i % 2 == 0)
System.out.println(Thread.currentThread().getName() + ":" + i);
// 返回当前线程的名字,默认从Thread-0开始
}
}
}
class MyThread2 extends Thread {
public void run() {
for(int i = 0 ; i < 100 ; i++) {
if(i % 2 != 0)
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
6,线程常用方法
package com.atguigu.java;
/**
* Thread类中常用方法
* start():启动一个线程并调用当前线程的run()
* run():子类通常需要重写Thread类中的此方法,将创建线程后要执行的操作写在此方法中
* currentThread():静态方法。返回当前线程
* getName():获取线程的名字
* setName():设置线程的名字
* yield():释放当前CPU的执行权
* join():线程a在执行时调用了线程b的join方法,此时a进入阻塞状态,直到b执行完后,a才结束阻塞状态
* stop():强制结束当前线程,已过时
* sleep():让当前线程睡眠指定的毫秒数,这段时间里线程是阻塞状态
* isAlive():判断当前线程是否存活,返回true或false
*/
public class ThreadMethodTest {
public static void main(String[] args) {
HelloThread h1 = new HelloThread(); // 创建对象的过程中会调用父类的构造器,父类构造器中
// 将代表线程名的属性设置为“Thread+n”
// 创建的是一个子类对象,Thread.currentThread()返回线程
h1.setName("线程一"); // 在启动一个线程并执行run()之前将它改名。调用set()方法给name属性赋值
h1.start(); // 当执行start()后,h1实际就是开启的这个线程。h1和Thread.currentThread()是同一对象
HelloThread h2 = new HelloThread("Thread: 2"); // 通过构造器直接给name属性赋值
h2.start();
try {
h2.join(); // 可能会抛异常。h1、h2、main三条线程都在执行,当执行到main的这条语句时,
// main被阻塞,下面的语句不再执行,必须等到h2执行完。这个过程h1和h2之间
// 可能也发生了切换
}catch(InterruptedException e) {
System.out.println(e.getMessage());
}
System.out.println(h2.isAlive()); // false,由于调用了h2的join(),h2先执行完
Thread.currentThread().setName("主线程"); // 将main()代表的主线程改名
System.out.println(Thread.currentThread().getName());
}
}
class HelloThread extends Thread {
@Override
public void run() {
for(int i = 0 ; i < 100 ; i++) {
if(i % 2 == 0)
try {
sleep(1000); // 让当前线程睡1000毫秒。可能抛出异常,不能用throws的方式
// 处理异常,因为父类Thread中的run()没有throws异常
}catch(InterruptedException e) {
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName() + ":" + i);
if(i % 20 == 0) {
yield(); // 当该线程正在执行并且i被20整除时,调用yield()使CPU切换当前线程,所以
// 下一个被执行的线程可能是任何一个,包括该线程自己
}
}
}
public HelloThread() {
}
public HelloThread(String name) { // 父类中的重载构造器
super(name);
}
}
7,线程优先级的设置
package com.atguigu.java;
/**
* 线程优先级
* MAX_PRIORITY:10 MIN_PRIORITY:1 NORM_PRIORITY:5(默认优先级)
* getPriority():获取当前线程的优先级
* setPriority():设置当前线程的优先级
*/
public class ThreadMethodTest1 {
public static void main(String[] args) {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY); // 将主线程优先级设置为最低,要设置为1、5、10外的其他优先级时直接写数字即可
HelloThread1 h1 = new HelloThread1();
h1.setPriority(Thread.MAX_PRIORITY); // 将分线程优先级设置为最高。不是优先级高的就一定先执行完,而是优先级高的先执行的概率大于优先级低的
h1.start();
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority());
}
}
class HelloThread1 extends Thread {
@Override
public void run() {
for(int i = 0 ; i < 100 ; i++) {
if(i % 2 == 0)
System.out.println(getName() + ":" + getPriority() + ":" + i); // 省略了this.,this可以指h1也可以指Thread.currentThread()
}
}
}
8,例题:多窗口卖票(继承Thread方式)
package com.atguigu.java;
/**
* 存在线程安全问题,有可能多个线程取到同一个ticket值
*/
public class WindowTest {
public static void main(String[] args) {
Window t1 = new Window();
Window t2 = new Window();
Window t3 = new Window();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Window extends Thread {
private static int ticket = 100;
@Override
public void run() {
while(true) {
if(ticket > 0) {
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
}else {
break;
}
}
}
}
9,创建多线程的方式二:实现Runnable接口
package com.atguigu.java;
/**
* 实现Runnable接口的方式创建多线程的过程
* 1,创建一个实现了Runnable接口的类,并在该类中实现接口中的抽象方法run()
* 2,创建实现类的对象,并将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 3,通过Thread类的对象调用start()
*/
public class ThreadTest1 {
public static void main(String[] args) {
MThread mThread = new MThread();
Thread t1 = new Thread(mThread);
t1.setName("线程一");
t1.start(); // 开启了t1这个线程。调用的是Thread类的start(),接着调用的是当前线程的run(),也
// 就是Thread的run(),查看源码,MThread赋值的属性叫target,Thread的run()调用
// 的就是target.run(),所以最终调用的是重写后的run()
Thread t2 = new Thread(mThread);
t2.setName("线程二");
t2.start(); // 开启第二个线程
}
}
class MThread implements Runnable {
@Override
public void run() {
for(int i = 0 ; i < 100 ; i++) {
if(i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i); // 没有继承Thread类,就没有getName()等方法
}
}
}
}
10,例题:多窗口卖票(实现Runnable接口方式)
package com.atguigu.java;
/**
* 存在线程安全问题,有可能多个线程取到同一个ticket值
*/
public class WindowTest1 {
public static void main(String[] args) {
Window1 w = new Window1();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start(); // 不用将ticket定义为static,因为只创建了一个对象,不论是哪个start(),最终调用的都是w.run()
}
}
class Window1 implements Runnable {
private int ticket = 100;
@Override
public void run() {
while(true) {
if(ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}else {
break;
}
}
}
}