一、线程入门
1.1 线程和进程
进程
-
一个正在运行的应用程序
- 安装到电脑中的程序,如果没有被运行不能称为进程
-
一个进程能同时执行多个任务
-
每一个任务可以看做一个线程
线程
- 进程的组成部分,称为轻量级进程
- 线程是进程中具体执行任务的部分
1.2 线程的组成
CPU时间片
- 指定时间内分配给线程的CPU资源
数据
- 程序处理的数据
堆数据
- 整个程序中所有线程共享的数据
栈数据
- 每个线程独有的数据
逻辑代码
- 驱动程序运行的代码
二、创建线程
2.1 继承Thread
1、创建类继承Thread
2、重写run方法
3、创建Thread子类对象
4、调用start方法启动线程
- Thread子类
/**
* Thread及其子类对象在Java中被称为线程对象
*
*/
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
- 创建线程并启动
public class Demo01 {
public static void main(String[] args) {
/**
* 1、创建类继承Thread
* 2、重写run方法
* 3、创建线程对象
* 4、启动线程
*/
MyThread t01 = new MyThread();
MyThread t02 = new MyThread();
MyThread t03 = new MyThread();
MyThread t04 = new MyThread();
t01.start();
t02.start();
t03.start();
t04.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
2.2 实现Runnable
1、创建类实现Runnable接口
2、重写run方法
3、创建Runnable实现类对象
4、创建Thread对象
5、启动线程
public class Demo01 {
public static void main(String[] args) {
/**
* 1、创建类实现Runnable
* 2、重写run方法
* 3、创建Runnable实现类对象【任务】
* 4、创建线程对象,加入任务
* 5、调用start方法启动线程
*/
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
/**
* Runnable实现类
* 具体的线程任务
*/
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
三、常用方法
3.1 name和id
public class Demo01 {
public static void main(String[] args) {
/**
long getId()
返回该线程的标识符。
String getName()
返回该线程的名称。
void setName(String name)
改变线程名称,使之与参数 name 相同。
static Thread currentThread()
返回对当前正在执行的线程对象的引用。
static void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
*/
MyThread01 t = new MyThread01("一号线程");
// 设置线程名称
t.setName("壹号线程");
t.start();
}
}
class MyThread01 extends Thread{
public MyThread01() {
super();
}
public MyThread01(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===" + Thread.currentThread().getId() + "===>" + i);
}
}
}
3.2 休眠
public class Demo02 {
public static void main(String[] args) throws InterruptedException {
for (int i = 10; i > 0; i--) {
System.out.println("发射倒计时:" + i);
// 休眠指定毫秒值的时间
Thread.sleep(1000);
}
System.out.println("发射");
}
}
3.3 优先级
public class Demo03 {
public static void main(String[] args) {
/**
void setPriority(int newPriority)
更改线程的优先级。
*/
// 创建线程对象
MyThread02 t01 = new MyThread02();
MyThread02 t02 = new MyThread02();
// 设置线程名字
t01.setName("一号线程");
t02.setName("222号线程");
// 设置线程优先级
t01.setPriority(1);
t02.setPriority(10);
// 启动线程
t01.start();
t02.start();
}
}
class MyThread02 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
3.4 插队
public class Demo04 {
public static void main(String[] args) throws InterruptedException {
/**
void join()
等待该线程终止。
*/
MyThread03 t = new MyThread03();
t.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
if (i == 10) {
// 子线程t插队
t.join();
}
}
}
}
class MyThread03 extends Thread {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
3.5 守护线程
public class Demo05 {
public static void main(String[] args) {
/**
void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。
*/
// 创建线程
MyThread04 t = new MyThread04();
// 设置线程名字
t.setName("后台线程");
// 把设置为守护线程
t.setDaemon(true);
// 启动线程
t.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
class MyThread04 extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
3.6 礼让线程
public class Demo06 {
public static void main(String[] args) {
/**
static void yield()
暂停当前正在执行的线程对象,并执行其他线程。
*/
MyThread05 t = new MyThread05();
t.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
if (i%10 == 0) {
// 每逢10礼让一次
Thread.yield();
}
}
}
}
class MyThread05 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
四、多线程卖票【掌握】
4.1 继承Thread
public class Demo01 {
public static void main(String[] args) {
/**
* 火车站四个窗口,共同卖出100张车票,用多线程实现。
*/
// 创建线程
TicketThread t01 = new TicketThread();
TicketThread t02 = new TicketThread();
TicketThread t03 = new TicketThread();
TicketThread t04 = new TicketThread();
// 设置名字
t01.setName("一号窗口");
t02.setName("22号窗口");
t03.setName("叁号窗口");
t04.setName("④号窗口");
// 启动线程
t01.start();
t02.start();
t03.start();
t04.start();
}
}
class TicketThread extends Thread {
private static int ticket = 100;
@Override
public void run() {
while (true) {
if (ticket <= 0) {
break;
}
// 一号==100,二号==100,三号==100,四号==100
ticket--;
// 一号==99,二号==98,三号==97,四号==96
System.out.println(Thread.currentThread().getName() + "卖出了第" + (100-ticket) + "张票,还剩下" + ticket);
}
}
}
4.2 实现Runnable
public class Demo02 {
public static void main(String[] args) {
// 创建卖票的任务,只需要创建一次
TicketRunnable r = new TicketRunnable();
// 创建4个线程
Thread t01 = new Thread(r, "111号窗口");
Thread t02 = new Thread(r, "222号窗口");
Thread t03 = new Thread(r, "333号窗口");
Thread t04 = new Thread(r, "444号窗口");
// 启动线程
t01.start();
t02.start();
t03.start();
t04.start();
}
}
class TicketRunnable implements Runnable {
int ticket = 100;
@Override
public void run() {
while (true) {
if (ticket <= 0) {
break;
}
ticket--;
System.out.println(Thread.currentThread().getName() + "卖出了第" + (100-ticket) + "张票,还剩下" + ticket);
}
}
}
五、多线程修改数组【掌握】
5.1 数组资源
import java.util.Arrays;
/**
* 数组资源
*/
public class ArrSrc {
String[] arr = new String[5];
int index = 0;
@Override
public String toString() {
return "ArrSrc [arr=" + Arrays.toString(arr) + ", index=" + index + "]";
}
}
5.2 线程A和B
- ThreadA
public class ThreadA extends Thread {
private ArrSrc arrSrc;
public ThreadA(ArrSrc arrSrc) {
super();
this.arrSrc = arrSrc;
}
@Override
public void run() {
// 存入数据
arrSrc.arr[arrSrc.index] = "Hello";
// 索引递增
arrSrc.index++;
}
}
- ThreadB
public class ThreadB extends Thread {
private ArrSrc arrSrc;
public ThreadB(ArrSrc arrSrc) {
super();
this.arrSrc = arrSrc;
}
@Override
public void run() {
// 存入数据
arrSrc.arr[arrSrc.index] = "World";
// 索引递增
arrSrc.index++;
}
}
5.3 测试
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
/**
* 创建类
* 测试类
* 数组资源类
* 数组
* 索引
* 线程A
* 存入Hello
* 线程B
* 存入World
*/
// 创建数组和索引
ArrSrc arrSrc = new ArrSrc();
// 创建线程A和B
ThreadA a = new ThreadA(arrSrc);
ThreadB b = new ThreadB(arrSrc);
// 启动线程修改数组
a.start();
b.start();
Thread.sleep(10);
// 输出修改之后的数组
System.out.println(arrSrc);
}
}
六、线程安全【掌握】
6.1 问题
- 当多个线程操作同一个共享资源的时候,如果操作分为多行代码执行
- 在a线程执行期间时间片到期,b线程继续执行,如果往复执行可能出现线程安全问题
6.2 同步代码块
-
synchronized修饰的代码块称为同步代码块
-
能确保原子操作不被破坏
- a线程执行同步代码块期间,其他线程不能争夺时间片
- a线程同步代码块执行结束之后,多条线程再同时争夺时间片
-
同步锁
- 多个线程共享的一个对象,线程把这个对象当做一个标记
- 任何一条线程执行到同步代码块的时候获取这个锁对象,其他线程进入阻塞状态
- 锁对象必须是对于多条线程惟一的对象
class TicketThread extends Thread {
private static int ticket = 100;
// 全局唯一的对象
private static final Object LOCK = new Object();
@Override
public void run() {
while (true) {
synchronized (LOCK) {
if (ticket <= 0) {
break;
}
// 一号==100
ticket--;
// 一号==99
System.out.println(Thread.currentThread().getName() + "卖出了第" + (100-ticket) + "张票,还剩下" + ticket);
}
}
}
}
6.3 同步方法
- 使用synchronized修饰方法
- 方法变成同步方法,只有被CPU分配了时间片的线程才能执行这个方法,执行期间不会切换其他线程
- 需要线程执行结束这个方法才会释放锁对象,进入就绪状态,再次等待分配时间片
import java.util.Arrays;
/**
* 数组资源
*/
public class ArrSrc {
String[] arr = new String[5];
int index = 0;
/**
* 修改数组的方法
* 修改数组数据
* 索引自增
* @param str
*/
public synchronized void modifyArr(String str) {
arr[index] = str;
index++;
}
@Override
public String toString() {
return "ArrSrc [arr=" + Arrays.toString(arr) + ", index=" + index + "]";
}
}
七、线程状态
7.1 线程状态
-
线程状态。线程可以处于下列状态之一:
NEW
至今尚未启动的线程处于这种状态。RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态。WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。TERMINATED
已退出的线程处于这种状态。
-
在给定时间点上,一个线程只能处于一种状态。