上述的图有些简略,下面详细说明下,线程共有6种状态:
new,runnable,blocked,waiting,timed waiting,terminated
1,当进入synchronized同步代码块或同步方法时,且没有获取到锁,线程就进入了blocked状态,直到锁被释放,重新进入runnable状态
2,当线程调用wait()或者join时,线程都会进入到waiting状态,当调用notify或notifyAll时,或者join的线程执行结束后,会进入runnable状态
3,当线程调用sleep(time),或者wait(time)时,进入timed waiting状态,
当休眠时间结束后,或者调用notify或notifyAll时会重新runnable状态。
4,程序执行结束,线程进入terminated状态
注意:
blocked,waiting,timed waiting 我们都称为阻塞状态
上述的就绪状态和运行状态,都表现为runnable状态
1 进程与线程的概念
进程是受操作系统管理的基本运行单元,线程可以理解成是在进程中独立运行的子任务。注意:多线程是异步的,所以千万不要把代码的书写顺序当成线程执行的顺序,线程的调用的时机是随机的。根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
多线程的好处:可以提高 CPU 的利用率。在多线程程序中,一个线程必须等待的时候,CPU 可以运行其它的线程而不是等待,这样就大大提高了程序的效率。也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
多线程的劣势:线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;多线程需要协调和管理,所以需要 CPU 时间跟踪线程;线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题。上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。
2什么是上下文切换?
当前任务在执行完 CPU 时间片切换到另一个任务之前会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。
3 线程创建的四种方式
创建线程有四种方式:
1 继承 Thread 类;
2 实现 Runnable 接口;
3 实现 Callable 接口;
4 使用 Executors 工具类创建线程池**【ThreadPool来创建】**
3.1 继承Thread
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("运行结束!");
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("MyThread");
}
}
注意:代码的运行结果与代码的执行顺序或调用顺序是无关的,需要注意的是执行start()方法的顺序不代表线程启动的顺序。
3.2 实现 Runnable
public class Run {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
System.out.println("运行结束!");
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("running...");
}
}
Thread继承了Runnable接口:
public class Thread extends Object implements Runnable
可以看出Thread(Runnable target)不仅可以传入 Runnable接口的对象,还可以传入Thread类的对象。
4守护线程和用户线程有什么区别呢?
用户 (User) 线程:运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程
守护 (Daemon) 线程:运行在后台,为其他前台线程服务。也可以说守护线程是 JVM 中非守护线程的 “佣人”。一旦所有用户线程都结束运行,守护线程会随 JVM 一起结束工作
5形成死锁的四个必要条件是什么
1 互斥条件:线程(进程)对于所分配到的资源具有排它性,即一个资源只能被一个线程(进程)占用,直到被该线程(进程)释放
2 请求与保持条件:一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
3 不剥夺条件:线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
4 循环等待条件:当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞
6什么是死锁?
死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。
7如何避免线程死锁
破坏互斥条件:这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。
破坏请求与保持条件:一次性申请所有的资源。
破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
破坏循环等待条件:靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
1 非共享数据
public class Run {
public static void main(String[] args) {
MyThread a = new MyThread("A");
MyThread b = new MyThread("B");
MyThread c = new MyThread("C");
a.start();
b.start();
c.start();
}
}
class MyThread extends Thread {
private int count = 5;
public MyThread(String name) {
super(name);
this.setName(name);
}
@Override
public void run() {
super.run();
while (count > 0) {
count--;
System.out.println("由" + this.currentThread().getName() + "计算,count=" + count);
}
}
}
2 共享数据
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread a = new Thread(myThread, "A");
Thread b = new Thread(myThread, "B");
Thread c = new Thread(myThread, "C");
Thread d = new Thread(myThread, "D");
Thread e = new Thread(myThread, "E");
a.start();
b.start();
c.start();
d.start();
e.start();
}
}
class MyThread extends Thread {
private int count = 5;
@Override
public void run() {
super.run();
while (count > 0) {
count--;
System.out.println("由" + this.currentThread().getName() + "计算,count=" + count);
}
}
}
3 System.out.println()的线性安全问题
System.out.println源码:
public void println(String x) {
synchronized (this) {
//print内部是是加锁的
print(x);
newLine();
}
}
public static void main(String[] args) {
MyThread run = new MyThread();
Thread a = new Thread(run);
Thread b = new Thread(run);
Thread c = new Thread(run);
Thread d = new Thread(run);
Thread e = new Thread(run);
a.start();
b.start();
c.start();
d.start();
e.start();
}
}
class MyThread extends Thread {
private int i = 5;
@Override
public void run() {
System.out.println("i=" + (i--) + "threadName=" + Thread.currentThread().getName());
}
}
i=4threadName=Thread-3
i=3threadName=Thread-2
i=1threadName=Thread-5
i=5threadName=Thread-1
i=2threadName=Thread-4
注意:print()方法内部是加锁、同步的,但是 i = i - 1的操作是在print()之外。
4 currentThread()
注意:构造方法是在对象的创建过程中调用的,即在MyThread myThread = new MyThread()阶段调用,是由主线程main调用的。
5 isAlive()
public final boolean isAlive() 测试这个线程是否活着。 结果 :true如果这个线程还活着; 否则false。
6 sleep()
当前正在执行的线程指的是this.currentThread()返回的线程
7 getId()
public long getId()返回此线程的标识符。
8 停止线程
public void interrupt()中断这个线程。
2.8.1 异常法
注意:thread.interrupt(),但是没有中断thread线程的run(),还是运行:System.out.println(“If this code is for and continues to run, the thread does not stop.”)。
注意:通过throw new InterruptedException() 的方法就实现停止线程。
2.8.2 stop停止线程
已经弃用的方法,stop释放锁会造成数据不一致。
2.8.3 判断线程是否停止状态
1 interrupted
源码
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
interrupted()是静态方法,内部实现是调用的当前线程的isInterrupted(),
并且会重置当前线程的中断状态,**与t.interrupted()的t是无关的。**
是否中断1? = false
是否中断2? = false
end!
2 isInterrupted
源码
public boolean isInterrupted() {
return isInterrupted(false);
}
isInterrupted()是实例方法,是调用该方法的对象所表示的那个线程的,不会重置当前线程的中断状态。**与t.isInterrupted()的t是有关的。**
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(600);
thread.interrupt();
System.out.println("是否中断1? = " + thread.isInterrupted());
System.out.println("是否中断2? = " + thread.isInterrupted());
}catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
class MyThread extends Thread {
@Override
public void run() {
for (int i= 0 ; i < 50000 ; i++) {
System.out.println("i=" + i);
}
}
}
i=45576
i=45577
是否中断1? = true
是否中断2? = true
end!
i=45578
i=45579
i=45580
特别注意:中断与停止的问题
9 暂停线程
suspend():暂停线程
resume():恢复线程
suspend与resume的缺点——独占
10 yield()
放弃当前的cpu资源,将它让给其他任务去占用CPU执行时间,但放弃的时间不确定,有可能刚刚放弃,马上又获得cpu时间片。
11 线程的优先级
public final void setPriority(int newPriority)更改此线程的优先级。
参数
newPriority - 设置此线程的优先级
Thread.MIN_PRIORITY = 1
Thread.MAX_PRIORITY = 10
Thread.NORM_PRIORITY = 5
异常
IllegalArgumentException - 如果优先级不在 MIN_PRIORITY到 MAX_PRIORITY 。
SecurityException - 如果当前线程不能修改此线程。
注意:CPU尽量是把执行资源让给优先级较高的线程,优先级具有随机性。
12 守护线程
守护线程是一个特殊的线程,他的特性有“陪伴”的含义,当进程中不存在非守护线程,则守护线程自动销毁。