线程
进程的一个执行路径,CPU基本调度单位。
进程由多个线程组成,彼此间完成不同的工作,交替执行,称为多线程。
单核:宏观同时执行,微观串行(顺序交替执行)
多核:真正意义上的并行
进程和线程的区别
• 进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。
• 一个程序运行后至少有一个进程。
• 一个进程可以包含多个线程,但是至少需要有一个线程。
• 进程间不能共享数据段地址,但同进程的线程之间可以。
线程的组成
• CPU时间片:操作系统(OS)会为每个线程分配执行时间。
• 运行数据:
1. 堆空间:存储线程需使用的对象,多个线程可以共享堆中的对象。
2. 栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈。
• 线程的逻辑代码。
创建线程
run()方法不能抛出异常;
主线程能够创建子线程
1.继承Tread的类,重写run()方法
Java虚拟机执行程序会自动创建一个主线程(main),可以使用主线程创建子线程
启动前程,使用start()方法,不能使用run(),使用run相当于主线程调用类的方法,这种只有主线程在执行。
获取线程ID和名称
名称可修改,ID只能获取
获取方式:
第一种方式:getId() getName()
第二种方式:Thread.cunrrentThread.getId() Thread.cunrrentThread.getName ()
第二种方法较好,使用灵活
修改线程名称:
第一种:setName()
第二种:构造方法 super(name)
public MyThread1(String name){
super(name);
}
内存结构
线程创建时内存(线程没启动,各自的栈还没生成)
线程启动,创建各自的栈空间,各自在自己的栈空间内执行自己的run方法
虚线不是指向,而是表示在操作这个变量
2.实现Runnable接口 优先选择
可运行类(封装了了线程要执行的功能,任务)
实现Runnable接口,就相当于任务,把任务交给线程去处理,所以既能一起处理(共享资源),也可以单独处理(独享资源)。而继承Thread,只能单独处理(独享资源)。所以优先选择。
thread的run方法:
public class TestRunnable {
public static void main(String[] args) {
Ticket1 ticket1 = new Ticket1();
Thread chichi1 = new Thread(ticket1, "chichi1");
Thread chichi2 = new Thread(ticket1, "chichi2");
Thread chichi3 = new Thread(ticket1, "chichi3");
Thread chichi4= new Thread(ticket1, "chichi4");
chichi1.start();
chichi2.start();
chichi3.start();
chichi4.start();
}
}
class Ticket1 implements Runnable{
private int ticket=100;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (ticket<=0){
break;
}
else {
System.out.println(Thread.currentThread().getName()+"卖出了"+count);
ticket--;
}
}
}
}
四个窗口共卖100张票,资源共享;
四个线程的target都指向ticket
使用实现类创建了线程对象,start启动线程,创建各自的栈空间
虚线不是指向,而是表示在操作这个变量
常见方法
• 休眠(静态)(有限等待)(需要抛出异常):
• public static void sleep(long millis) • 当前线程主动休眠 millis 毫秒。
休眠之后,不会争取CPU,休眠时间到了,进入就绪状态,参与CPU的争抢
线程的sleep方法应该写在线程的run()方法里,就能让对应的线程睡眠。sleep方法只能让当前线程睡眠。调用线程实例对象t1.sleep(),睡眠的不是t1这个实例类,而是当前线程。
public class Testsleep {
public static void main(String[] args) {
Runnable r=new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(i+"chichi");
}
}
};
Thread thread=new Thread(r);
thread.start();
System.out.println(thread.currentThread().getName());
try {
thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5; i++) {
System.out.println(i+"main");
}
}
}
输出
虽用实例对象调用静态方法,但实际静态方法都指向的是当前对象,故使用静态方法应都写在run方法内部
• 放弃:
• public static void yield() • 当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片。
放弃,谦让,把CPU的使用权让给别的线程(优先级和让出线程相同或高于让出线程),进入就绪状态,继续参与CPU的争抢。(可能会再次抢到)
• 加入:(需要抛出异常)(无限等待)
• public final void join() • 允许其他线程加入到当前线程中。
要加入的线程使用join()方法,被加入的阻塞
当前线程会阻塞,直到加入线执行结束
sleep,join要放在try,catch中使用,被捕捉错误,sleep方法中断,会抛出异常
优先级
setPriority()方法 设置线程的优先级
优先级高的线程表示获得cpu时间片的机会多,可在任何地方设置优先级
线程打断
线程对象.interrupt()
打断线程,被打断线程抛出InterruptedExceotion异常,只有能抛出这个异常的方法才能被打断。
守护线程
默认情况下,创建的线程都是前台线程。
要在线程开始之前设置,否则会出错
线程状态
线程安全
线程同步
1.同步代码块
临界资源对象必须是这些线程都能访问到的,并且都要访问到的,并且已经进行了初始化的引用类型(并不是要修改的值)
一般选择共享资源作为锁,多个线程必须使用同一个锁
加锁就相当于封装了一个资源或者是操作。使用完才释放。
得不到锁标记,进入阻塞状态。
线程的状态(阻塞) jdk1.5之后,就绪状态和运行状态合二为一。
一共六种
getState()方法,可返回当前状态
2.同步方法
非静态方法,锁的是this,方法结束后,会释放锁
静态方法,锁是 类.class(锁这个类对象)
synchronized就是针对内存区块申请内存锁,this关键字代表类的一个对象,所以其内存锁是针对相同对象的互斥操作,而static成员属于类专有,其内存空间为该类所有成员共有,这就导致synchronized()对static成员加锁,相当于对类加锁,也就是在该类的所有成员间实现互斥,在同一时间只有一个线程可访问该类的实例。