第一章.创建线程的方式
1.第一种方式_extends Thread
1.定义一个自定义线程类继承Thread
2.重写run方法(run方法是用于设置线程任务的)
3.创建自定义线程类对象
4.调用Thread类中的start方法(start方法:开启线程,jvm自动执行run方法)
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("MyThread...执行了"+i);
}
}
}
public class Test01 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();//开启线程,jvm自动执行run方法
for (int i = 0; i < 10; i++) {
System.out.println("Main线程...执行了"+i);
}
}
}
2.Thread类中的方法
void start() -> 开启线程,jvm自动调用run方法
void run() -> 设置线程任务,这个run方法是Thread重写的接口Runnable中的run方法
String getName() -> 获取线程名字
void setName(String name) -> 给线程设置名字
static Thread currentThread() -> 获取正在执行的线程对象(此方法在哪个线程中使用,获取的就是哪个线程对象)
static void sleep(long millis)->线程睡眠,超时后自动醒来继续执行,传递的是毫秒值
问题:为啥在main方法中处理异常可以throws,在run方法中处理异常不能throws,只能try?
Thread中的run方法没有抛异常,所以我们重写之后不能throws,只能try
void setPriority(int newPriority) -> 设置线程优先级,优先级越高的线程,抢到CPU使用权的几率越大,但是不是每次都先抢到
int getPriority() -> 获取线程优先级
void setDaemon(boolean on) -> 设置为守护线程,当非守护线程执行完毕,守护线程就要结束,但是守护线程也不是立马结束,当非守护线程结束之后,系统会告诉守护线程人家结束了,你也结束吧,在告知的过程中,守护线程会执行,只不过执行到半路就结束了
static void yield() -> 礼让线程,让当前线程让出CPU使用权
void join() -> 插入线程或者叫做插队线程
3.第二种方式_实现Runnable接口
1.创建自定义线程类,实现Runnable接口
2.重写run方法,设置线程任务
3.利用Thread的有参构造:Thread(Runnable r),创建对象,传递自定义线程类对象
4.调用Thread中的start方法,开启线程
public class Test01 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable, "赵四");
Thread t2 = new Thread(myRunnable, "广坤");
t1.start();
t2.start();
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"...执行了"+i);
}
}
}
1.继承的方式是有局限性的,因为继承只支持单继承,不能多继承,如果使用继承的方式,这个自定义线程类还有其他的父类,此时就没办法再继承Thread了
2.实现的方式没有局限性,如果自定义线程类有父类,它可以直接继承一个父类的同时实现Runnable接口
4.匿名内部类创建多线程
public static void main(String[] args) {
/*
Thread(Runnable r)
*/
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"...执行了"+i);
}
}
},"赵四").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"...执行了"+i);
}
}
},"刘能").start();
}
第二章.线程安全
什么时候发生:当多个线程共同操作同一个资源时,会出现线程安全问题
出现的原因:CPU在多个线程之间做高速切换
1.解决线程安全问题的第一种方式(使用同步代码块)
1.格式:
synchronized(锁对象){
可能出现线程安全问题的代码 }
2.锁对象:任意对象
3.执行流程:
线程抢到锁,去执行,其他线程必须在同步代码块中排队等待,等着执行的线程出了同步代码块,相当于释放锁了,其他排队等待的线程才能抢到锁去执行
4.注意:
想要实现线程安全(线程同步),多个线程之间必须使用同一把锁
public class MyTicket implements Runnable{
int ticket = 100;
//创建一个对象,充当锁对象
Object obj = new Object();
@Override
public void run() {
while(true){
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (obj){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+"...买了第"+ticket+"张票");
ticket--;
}
}
}
}
}
public class Test01 {
public static void main(String[] args) {
MyTicket myTicket = new MyTicket();
Thread t1 = new Thread(myTicket, "赵四");
Thread t2 = new Thread(myTicket, "刘能");
Thread t3 = new Thread(myTicket, "广坤");
t1.start();
t2.start();
t3.start();
}
}
2.解决线程安全问题的第二种方式:同步方法
2.1.普通同步方法_非静态
1.格式:
修饰符 synchronized 返回值类型 方法名(形参){
方法体
return 结果
}
2.默认锁:this
public void method01(){
synchronized(this){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+"...买了第"+ticket+"张票");
ticket--;
}
}
}
2.2.静态同步方法
1.格式:
修饰符 static synchronized 返回值类型 方法名(形参){
方法体
return 结果
}
2.默认锁:当前类.class
public static void method01(){
synchronized(MyTicket.class){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+"...买了第"+ticket+"张票");
ticket--;
}
}
}
死锁指的是两个或者两个以上的线程在执行的过程中由于竞争同步锁而产生的一种阻塞现象;如果没有外力的作用,他们将无法继续执行下去,这种情况称之为死锁
第三章.线程状态
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,有几种状态呢?在API中java.lang.Thread.State这个枚举中给出了六种线程状态: