一、线程的相关概念
程序:为完成特定任务、用某种语言编写的一组指令的集合,就是我们写的代码。
进程:指运行中的程序,进程是程序的一次执行过程,或是正在运行的一个程序,是动态过程:有它自身的产生、存在和消亡的过程。
线程:线程由进程创建,是进程一个实体,一个进程可以拥有多个线程。
单线程:同一时刻,只允许执行一个线程
多线程:同一时刻,可以执行多个线程
并发:同一时刻,多个任务交替执行(单核CPU)
并行:同一时刻,多个任务同时执行(多核CPU)
二、线程的基本使用
创建线程
1.继承 Thread类,重写run方法
public class info {
public static void main(String[] args) {
car car = new car();
car.start();
}
}
class car extends Thread{
@Override
public void run() {
boolean flag=true;
int cnt=0;
while (flag){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(++cnt);
}
}
}
2.实现Runnable接口,重写run方法
public class info {
public static void main(String[] args) {
car car = new car();
new Thread(car).start();
}
}
class car implements Runnable{
@Override
public void run() {
boolean flag=true;
int cnt=0;
while (flag){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(++cnt);
}
}
}
基本逻辑:程序开始,调用main主线程,之后创建线程,通过Thread.start()开始,再调用start0(),之后底层调用一系列操作后,调用run()方法,开始执行你的业务逻辑。
start()方法调用start0()方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态。具体什么时候执行,取决于CPU,由CPU统一调度。
线程终止
1.自动退出
2.通知方式——通过使用变量来控制run方法退出的方式
main是主线程,主线程消亡了,其他线程也可能继续执行
三、线程常用方法
setName,getName,设置和返回线程的名称
start,使该线程开始执行,Java虚拟机底层调用该线程的start0方法
run,调用线程对象run方法
setPriority,更改线程的优先级
getPriority,获取线程的优先级
sleep,在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
interrupt,中断线程
yield:线程的礼让,主动让出cpu,让其他线程执行,但礼让的时间不确定,也不一定礼让成功
join:线程的插队,插队的线程一旦成功,肯定先执行完插入的线程所有的任务。
public class info {
public static void main(String[] args) throws InterruptedException {
test1 test1 = new test1();
for (int i=1;i<=20;i++){
Thread.sleep(500);
System.out.println("main主线程:"+i);
if (i==5){
test1.start(); //启动子线程
test1.join(); //立即将test1线程插入到main线程,让test1执行
}
}
System.out.println("main主线程退出");
}
}
class test1 extends Thread{
@Override
public void run() {
int cnt=0;
while (true){
if (cnt>=20){
System.out.println("test1线程退出");
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("test1线程:"+(++cnt));
}
}
}
四、用户线程和守护线程
用户线程,也叫工作线程,当线程的任务执行完成或通知方式结束的线程
守护线程(Deamon),一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
常见的守护线程:垃圾回收机制
t.setDeamon(true) //将线程设置为守护进程
五、线程的生命周期,线程的七大状态
NEW,未启动
RUNNABLE,可执行状态
BLOCKED,阻塞
WAITING,等待
TIMED_WAITING,等待
TERMINATED,退出
六、线程同步
1.线程同步 概念:线程同步,当有一个线程对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。
2.同步具体方法 Synchronized
同步代码块synchronized(对象){ //得到对象的锁,才能操作同步代码
//被同步的代码
}
同步方法
public synchronized void sell(){ //同步方法,在同一时刻,只能由一个线程来执行sell方法
//被同步的代码
}
public class info {
public static void main(String[] args){
test test = new test();
new Thread(test).start();
new Thread(test).start();
new Thread(test).start();
}
}
class test implements Runnable{
private int cnt=100;
private boolean flag=true;
public synchronized void sell(){
if (cnt<=0){
System.out.println("线程结束,票卖完了,剩余票数:"+cnt);
flag=false;
return ;
}
try {
Thread.sleep(200);
System.out.println("出售,剩余票数: "+(--cnt));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public void run() {
while (flag){
sell();
}
}
}
七、互斥锁
Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完成性;
每个对象都对应一个可称为"互斥锁"的标记,这个标记用来保证在任意时刻,只能有一个线程访问该对象。
同步的局限性:导致程序的执行效率要降低;
同步方法(非静态static)的锁可以是this,也可以是其他对象(但是一定要是同一对象);
同步方法(静态static)的锁为当前类本身;
八、线程死锁,释放锁
有两个锁,A等待B完成同步方法,B等待A完成同步方法,两者相互等待,造成死锁。
释放锁:正常结束,被break,出现异常,执行wait()
不释放锁:执行同步代码块或同步方法时,调用 Thread.sleep()、Thread.yield()
执行同步代码块时,其他线程调用该线程的suspend()的方法,将该线程挂起
自己总结的小点:
线程间控制问题:
怎样才能让一个线程持有其他线程的属性?在这个线程所在的类创建其他线程的类的对象即可,在把其他线程传进去(线程本身也是类,也是对象),之后就能,修改其他线程的变量了。
线程间同步问题:
涉及到多个线程共享资源的问题,用实现Runnable的方式,比较方便
多个线程运行,可以运行同一个对象,也就是当多个线程的start0()调用后,可以都执行一个对象的run()方法;
多个线程运行,也可以运行同类的不同对象,不同类的不同对象。