目的:
1.学会如何创建一个线程:(1)通过继承Thread类 (2)通过实现Runnable接口
2.线程的同步:学会使用synchronized ReetrantLock
3.主线程和子线程之间使用接口回调数据
4.线程之间的通信:学会一下内容的使用
synchronized(wait notifi onrifiall)
ReetrantLock lock;
Condition c = lock.newCondition()
await single singleall
进程:正在运行的一个程序 例如:qq IDE 浏览器
系统会为这个进程分配独立的内存资源
线程:具体执行任务的最小单位
1.一个进程最少拥有一个线程 (主线程 运行起来就执行的线程)
2.线程之间是共享资源的(进程申请的)
3. 线程之间可以通信 (数据传递: 多数为主线程和子线程)
4. 每一个线程都有自己的运行回路 (生命周期)
线程的生命周期 (就也就是说线程的状态)
NEW :新建 线程刚被创建好
RUNNABLE 就绪状态 只要抢到时间片就可以运行这个线程
BLOCKED 阻塞状态 sleep wait
WAITING 等待
TIMED_WAITING
TERMINATED 终止
image.png
为什么需要创建子线程
如果在主线程中存在比较耗时的操作:下载视频 上传文件 数据处理
这些操作会阻塞主线程,后面的任务必须等这些任务执行完毕之后才能执行,用户体验比较差。因此,为了不阻塞主线程,需要将耗时的任务放在子线程中去处理。
如何创建一个子线程
1.定义一个类继承于Thread 实现run方法
注意:
- join : 让当前这个线程阻塞 等join的线程执行完毕再执行
- setName: 设置线程名称
- getName: 获取线程名称
- currentThread : 获取当前运行的线程对象
- start :开启任务
2.实现Runnable接口 实现run方法
a。 创建任务 创建实现Runnable接口
b. 使用Thread 为这个任务分配线程
c. 开启任务start
创建线程的第一种方式: 继承Thread
public class Documents {
public static void main(String [] args)throws NullPointerException{
TestThread testThread = new TestThread();
testThread.setName("阿中一号");
//开启任务
testThread.start();
//创建第二个线程
TestThread t1 = new TestThread();
t1.setName("阿中2号");
t1.start();
}
}
class TestThread extends Thread{
//实现run方法
//方法里面就是具体执行的代码
@Override
public void run() {
String name = Thread.currentThread().getName();
for (int i = 1; i <=100; i++) {
System.out.println(name+":"+i);
}
super.run();
}
}
创建线程的第二种方式: 实现Runnable接口
public class Documents {
public static void main(String [] args)throws NullPointerException{
//创建一个任务,实现Runnable接口
ZHTThread pt = new ZHTThread();
Thread t = new Thread(pt);
t.setName("天哥一号");
t.start();
Thread t2 = new Thread(pt);
t2.setName("天哥二号");
t2.start();
}
}
class ZHTThread implements Runnable{
@Override
public void run() {
for (int i = 1; i <=100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
使用方式二
//这个任务只需要使用一次的时候可选如下操作
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
});
t.setName("天哥一号");
t.start();
使用方式三
//创建线程的同时 直接开启线程任务
//不需要操作线程本身
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <=100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}).start();
使用方式4:lambda表达式
new Thread(()->{
for (int i = 1; i <=100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}).start();
线程同步
当多个线程操作同一个资源,线程无法确定自己什么时候被阻塞,这容易导致数据错误
image.png
如上图,无法确定正在执行的线程在执行到那一步线程被抢占。
synchronized Lock :加锁解锁
synchronized : 同步监听器
任何一个对象都有自己的一把锁,如果多个线程操作同一个代码块,并且需要同步 那么必须操作同一个对象/同一把锁。
image.png
*卖票任务代码:
static TestThread tt2;
public static void main(String [] args){
Ticket ticketCQ = new Ticket("重庆");
Thread t1 = new Thread(ticketCQ);
t1.start();
Ticket ticketSH = new Ticket("上海");
Thread t2 = new Thread(ticketSH);
t2.start();
}
//用于卖票的任务
class Ticket implements Runnable {
//定义所有的车票的数量
public static int num = 100;
String name;
public Ticket(String name) {
this.name = name;
}
static final Object obj = new Object();
@Override
//同步方法实际上就是同步代码块 同步方法里面同步的就是对象本身
public void run() {
for (int i = 1; i <= 100; i++) {
//判断有没有票
synchronized (obj) {
if (num > 0) {
System.out.println(name + "出票:" + num);
num--;
obj.notify();
try{
//当前线程等待
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
同步方法
同步方法实际上就是同步代码块 同步方法里面同步的就是对象本身
必须确保多个对象调用的同步方法操作的是同一个对象
//public synchronized void test()
//本质就是同步代码块
//等价于
// synchronized (this){
// test();
// }
//
image.png
使用ReentrantLock同步
public void run() {
for (int i = 1; i <= 100; i++) {
//加锁
lock.lock();
//判断有没有票
if (num > 0) {
System.out.println(name + "出票:" + num);
num--;
}
//解锁
lock.unlock();
}
}
await/wait sleep yield之间的区别
image.png
感悟:发现有个小毛病,上课本来听的很认真,都能听懂,但是偶尔会跑一下神,之后就凉凉了,感觉在听天书。。。。。不过还好讲的不是特别快,而且还有视频 ,但是总会浪费大量时间去弄懂上课讲的内容,等到要全部弄懂的时候已经比较晚了,又没有充足的时间敲代码。。。

image.png
image.png








网友评论