进程是一个应用程序
线程是一个进程中的执行场景/执行单元
一个进程可以启动多个线程
实现线程的第一种方式:
1编写一个类,直接继承java.lang.Thred,重写run方法
package com.test;
public class Test14 {
public static void main(String[] args) {
ThraedTest test = new ThraedTest();
//启动线程
//start()方法的作用:启动一个分支线程,在JVm中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了
//这段代码的任务只是开辟了一个栈空间,只要新的栈空间开出来,start()方法就结束了线程就启动成功
//启动成功的线程会自动调用run方法并且run方法在分支栈的栈底部(压栈)
//run方法在分支栈的栈底部,main方法在主栈的栈底部,run和main是平级的
test.start();
for (int i = 0; i < 1000; i++) {
System.out.println("主线程-->" + i);
}
}
}
class ThraedTest extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("分支线程-->" + i);
}
}
}
2编写一个类,实现java.lang.Runnable接口
package com.test;
public class Test15 {
public static void main(String[] args) {
MyTest myTest = new MyTest();
Thread thread = new Thread(myTest);
thread.start();
long l1 = System.currentTimeMillis();
for (int i = 0; i< 100; i ++) {
System.out.println("主线程->"+i);
}
long l2 = System.currentTimeMillis();
System.out.println(l1+"----"+l2);
}
}
class MyTest implements Runnable{
@Override
public void run() {
for (int i = 0; i< 100; i ++) {
System.out.println("分支线程->"+i);
}
}
}
线程的生命周期
刚new出来的线程对象属于新建状态当调用start方法时进入就绪状态(就绪状态的线程又叫做可运行状态,表示当前线程具有抢夺CPU时间片的权利<CPU时间片就是执行权>当一个线程抢夺CPU时间片之后,就开始执行run方法,run方法的开始执行标志着线程进入运行状态)
Run方法的开始执行标志着这个线程进入运行状态,当之前占有的CPU时间片用完之后,会重新回到就绪状态继续抢夺CPU时间片,当再次抢到CPU时间片之后,会重新进入run方法接着上一次的代码继续往下执行
线程对象的生命周期
新建状态 就绪状态 运行状态 阻塞状态 死亡状态
获取线程名字 getName()
设置线程名字 setName(“名字”)
当线程没有设置名字时,默认名字
Thread-0 Thread-1 Thread-2
获取当前线程对象
Thread.currentThread()
sleep 作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其他线程使用
Thread.sleep(2000)
interrupt中断睡眠,靠的是抛出异常
多线程并发环境下,数据安全问题
存在安全问题的条件:1多线程并发2有共享数据3共享数据有修改行为
如何解决 :线程排队执行(不能并发)
用排队执行解决线程安全问题这种机制被称为线程同步机制
线程同步机制语法
synchronized(){
//线程同步代码
synchronized后面小括号中传的这个“数据”必须是多线程共享的数据,才能达到多线程排队
参数为共享的对象
}
synchronized三种写法
1 同步代码块
synchronized(线程共享对象){
同步代码块
}
2在实例方法上使用synchronized表示共享对象一定是this并且同步代码块是整个方法体
3在静态方法上使用synchronized表示找类锁,类锁永远只有一把。