1.线程
是"进程"中某个单一顺序的控制流。也被称为轻量进程(lightweight processes)。计算机科学术语,指运行中的程序的调度单位。
一个进程必须至少有一个线程,通常称为主线程。
2.线程调度
计算机通常只有一个CPU,在任意时刻只能执行一条机器指令,每个线程只有获得CPU的使用权才能执行指令.所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得CPU的使用权,分别执行各自的任务.在运行池中,会有多个处于就绪状态的线程在等待CPU
JAVA虚拟机的一项任务就是负责线程的调度,线程调度是指按照特定机制为多个线程分配CPU的使用权.
Windows的调度是以线程为粒度调度的。调度的决策被严格限制在以线程为基础,并不考虑这个线程属于哪一个进程。当考虑到进程并不运行,而仅为其线程提供资源和运行的上下文环境时,这种方法就有意义了,例如,进程A有10个可运行的线程,进程B有2个可运行的线程,而且这12个线程的优先级别相同,那么,每一个线程将会使用1/12的CPU时间,而不是将CPU 50%的时间分配给进程A,50% 的时间分配给进程B。
有两种调度模型:分时调度模型和抢占式调度模型。
分时调度模型是指让所有的线程轮流获得cpu的使用权,并且平均分配每个线程占用的CPU的时间片这个也比较好理解。
java虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃CPU。
一个线程会因为以下原因而放弃CPU:
1 java虚拟机让当前线程暂时放弃CPU,转到就绪状态,使其它线程获得运行机会。
2 当前线程因为某些原因而进入阻塞状态
3 线程结束运行
需要注意的是,线程的调度不是跨平台的,它 不仅仅取决于java虚拟机,还依赖于操作系统。在某些操作系统中,只要运行中的线程没有遇到阻塞,就不会放弃CPU;在某些操作系统中,即使线程没有遇到阻塞,也会运行一段时间后放弃CPU,给其它线程运行的机会。
java的线程调度是不分时的,同时启动多个线程后,不能保证各个线程轮流获得均等的CPU时间片。
如果希望明确地让一个线程给另外一个线程运行的机会,可以采取以下办法之一。
调整各个线程的优先级
让处于运行状态的线程调用Thread.sleep()方法
让处于运行状态的线程调用Thread.yield()方法
让处于运行状态的线程调用另一个线程的join()方法
3.例子
反复运行并观察输出,其结果是不确定的。因为main主线程、垃圾回收线程,它们线程优先级相同,随机占用cpu
*/
4.创建线程方式一
1)定义类继承Thread类,覆盖run方法(线程所要执行的代码),将业务代码写在run方法中。
2)通过实例化,来创建线程,通过start启动线程,调用线程任务run方法
3)run和start区别
run()方法:在本线程内调用该Runnable对象的run()方法,可以重复多次调用;
start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程;
4)getName()与Thread.currentThread().getName()
每个继承Thread类的子类,在创建时都有自己的线程名称,都有getName()方法;
run函数中,getName()和Thread.currentThread().getName()是有区别的,前者是线程对象的名称,后者是当前运行线程的名称
5)例子
example01
输出结果可能不是有序的,原因多个线程同时执行,某个线程可能--num后,将值传给println方法,
执行println方法时,还没有来得及打印,但失去执行权,后来某个时刻又获取到执行权
*/
6)每条线程有自己的栈空间,意味着每个线程都有自己的局部变量
5.线程的五种状态
2)cpu的执行权:正在被cpu处理
3)睡眠和等待,设置线程执行资格与执行权
4)运行:既具备执行资格、又有执行权
5)cpu处理某线程时,该线程具备执行资格,执行权,其它线程不具备执行权,但具备执行资格,处于临时阻塞状态,等待执行权
6)睡眠和等待,都不具备
6.创建线程方式二
1)定义类实现Runnable接口,覆盖run方法,将线程的任务代码封装到run中
2)通过Thread创建线程对象,并将Runnable子类对象作为构造函数的参数传递
7.两种创建对比
继承Thread与实现Runnable接口,前者具备线程所有功能,强调is-a的关系,后者仅仅提供线程任务功能,强调has-a
Runnable好处:避免单继承的局限性,将任务从线程中的子类中分离,将任务封装成对象。
8.多线程安全问题
多个线程操作(修改)共享的数据,可能存在安全问题。
9.同步代码块
synchronized(对象)
{
}
好处:解决线程的安全问题
弊端:都会判断同步锁,降低效率
使用前提:多个线程使用同一个锁
3.同步代码块和同步函数区别
同步代码块的锁是任意的对象,同步函数的锁是this
4.静态同步函数的锁
使用的锁是该方法所在类的字节码文件对象,类名.class或this.getClass()
(字节码文件对象:JVM在加载某个.class文件之后得到的一段字节??还是就是.class本身?)
5.死锁
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
是"进程"中某个单一顺序的控制流。也被称为轻量进程(lightweight processes)。计算机科学术语,指运行中的程序的调度单位。
一个进程必须至少有一个线程,通常称为主线程。
2.线程调度
计算机通常只有一个CPU,在任意时刻只能执行一条机器指令,每个线程只有获得CPU的使用权才能执行指令.所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得CPU的使用权,分别执行各自的任务.在运行池中,会有多个处于就绪状态的线程在等待CPU
JAVA虚拟机的一项任务就是负责线程的调度,线程调度是指按照特定机制为多个线程分配CPU的使用权.
Windows的调度是以线程为粒度调度的。调度的决策被严格限制在以线程为基础,并不考虑这个线程属于哪一个进程。当考虑到进程并不运行,而仅为其线程提供资源和运行的上下文环境时,这种方法就有意义了,例如,进程A有10个可运行的线程,进程B有2个可运行的线程,而且这12个线程的优先级别相同,那么,每一个线程将会使用1/12的CPU时间,而不是将CPU 50%的时间分配给进程A,50% 的时间分配给进程B。
有两种调度模型:分时调度模型和抢占式调度模型。
分时调度模型是指让所有的线程轮流获得cpu的使用权,并且平均分配每个线程占用的CPU的时间片这个也比较好理解。
java虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃CPU。
一个线程会因为以下原因而放弃CPU:
1 java虚拟机让当前线程暂时放弃CPU,转到就绪状态,使其它线程获得运行机会。
2 当前线程因为某些原因而进入阻塞状态
3 线程结束运行
需要注意的是,线程的调度不是跨平台的,它 不仅仅取决于java虚拟机,还依赖于操作系统。在某些操作系统中,只要运行中的线程没有遇到阻塞,就不会放弃CPU;在某些操作系统中,即使线程没有遇到阻塞,也会运行一段时间后放弃CPU,给其它线程运行的机会。
java的线程调度是不分时的,同时启动多个线程后,不能保证各个线程轮流获得均等的CPU时间片。
如果希望明确地让一个线程给另外一个线程运行的机会,可以采取以下办法之一。
调整各个线程的优先级
让处于运行状态的线程调用Thread.sleep()方法
让处于运行状态的线程调用Thread.yield()方法
让处于运行状态的线程调用另一个线程的join()方法
3.例子
public class Demo {
public static void main(String[] args) {
new Test();
new Test();
new Test();
new Test();
System.gc(); //调用gc
System.out.println("hello world");
}
}
class Test
{
public void finalize(){
System.out.println("test clear");
}
}
/**
反复运行并观察输出,其结果是不确定的。因为main主线程、垃圾回收线程,它们线程优先级相同,随机占用cpu
*/
4.创建线程方式一
1)定义类继承Thread类,覆盖run方法(线程所要执行的代码),将业务代码写在run方法中。
2)通过实例化,来创建线程,通过start启动线程,调用线程任务run方法
3)run和start区别
run()方法:在本线程内调用该Runnable对象的run()方法,可以重复多次调用;
start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程;
4)getName()与Thread.currentThread().getName()
每个继承Thread类的子类,在创建时都有自己的线程名称,都有getName()方法;
run函数中,getName()和Thread.currentThread().getName()是有区别的,前者是线程对象的名称,后者是当前运行线程的名称
5)例子
example01
public class Demo2 {
public static void main(String[] args) {
Test2 t1=new Test2();
Test2 t2=new Test2();
//t1.run();
//t2.run();
t1.start();
t2.start();
System.out.println(Thread.currentThread().getName());
}
}
class Test2 extends Thread
{
public void run(){
for(int i=1;i<=10;i++){
System.out.println(Thread.currentThread().getName());
//System.out.println(getName()); //run或start结果一样,对象名
}
}
}
example02
public class Demo1 {
public static void main(String[] args) {
Ticket t1=new Ticket();
Ticket t2=new Ticket();
Ticket t3=new Ticket();
Ticket t4=new Ticket();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket extends Thread
{
private static int num=10;
public void run(){
while(num>0){
System.out.println(getName()+":"+--num);
}
}
}
/**
输出结果可能不是有序的,原因多个线程同时执行,某个线程可能--num后,将值传给println方法,
执行println方法时,还没有来得及打印,但失去执行权,后来某个时刻又获取到执行权
*/
6)每条线程有自己的栈空间,意味着每个线程都有自己的局部变量
5.线程的五种状态
创建、运行、消亡(run方法结束或stop())、睡眠(sleep)、等待(wait方法,对应notify唤醒方法)
1)cpu的执行资格:可以被cpu处理的线程,在处理队列中2)cpu的执行权:正在被cpu处理
3)睡眠和等待,设置线程执行资格与执行权
4)运行:既具备执行资格、又有执行权
5)cpu处理某线程时,该线程具备执行资格,执行权,其它线程不具备执行权,但具备执行资格,处于临时阻塞状态,等待执行权
6)睡眠和等待,都不具备
6.创建线程方式二
1)定义类实现Runnable接口,覆盖run方法,将线程的任务代码封装到run中
2)通过Thread创建线程对象,并将Runnable子类对象作为构造函数的参数传递
7.两种创建对比
继承Thread与实现Runnable接口,前者具备线程所有功能,强调is-a的关系,后者仅仅提供线程任务功能,强调has-a
Runnable好处:避免单继承的局限性,将任务从线程中的子类中分离,将任务封装成对象。
8.多线程安全问题
多个线程操作(修改)共享的数据,可能存在安全问题。
9.同步代码块
synchronized(对象)
{
}
好处:解决线程的安全问题
弊端:都会判断同步锁,降低效率
使用前提:多个线程使用同一个锁
===============================分割线==========================
1.同步函数
class Bank implements Runnable {
private int sum;
private Object obj = new Object();
public void add(int num) {
synchronized (obj) {
sum += num;
System.out.println("sum=" + sum);
}
}
public void run() {
for (int i = 0; i <= 3; i++) {
add(100);
}
}
}
/*add函数中存在同步代码块,可直接改写为同步函数,Object对象可以删除*/
public synchronized void add(int num) {
sum += num;
System.out.println("sum=" + sum);
}
2.同步函数设置在run函数上,会被某个线程一直占用,直到它执行结束,其他线程才能得到处理权
3.同步代码块和同步函数区别
同步代码块的锁是任意的对象,同步函数的锁是this
4.静态同步函数的锁
使用的锁是该方法所在类的字节码文件对象,类名.class或this.getClass()
(字节码文件对象:JVM在加载某个.class文件之后得到的一段字节??还是就是.class本身?)
5.死锁
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
简单演示
package lock;
public class Demo {
public static void main(String[] args) {
LockDemo d1=new LockDemo(true);
LockDemo d2=new LockDemo(false);
Thread t1=new Thread(d1);
Thread t2=new Thread(d2);
t1.start();
t2.start();
}
}
class LockDemo implements Runnable
{
private boolean flag;
LockDemo(boolean flag){
this.flag=flag;
}
public void run(){
if(flag)
{
synchronized(Lock.lockA)
{
System.out.println("true lockA");
synchronized(Lock.lockB)
{
System.out.println("true lockB");
}
}
System.out.println("true over");
}
else
{
synchronized(Lock.lockB)
{
System.out.println("false lockB");
synchronized(Lock.lockA)
{
System.out.println("false lockA");
}
}
System.out.println("false over");
}
}
}
class Lock
{
public static final Object lockA=new Object();
public static final Object lockB=new Object();
}