一、常见概念:
1.进程:每个进程都有独立的代码和数据空间(即独立的内存区域)(进程上下文),进程间的切换会有较大的开销,一个进程包含1~n个线程。
2.线程:一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并行多个线程,每条线程并行执行不同的任务。同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。
3.线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
4.多进程是指操作系统能同时运行多个任务(程序)。
5.多线程是指同一个程序中有多个顺序流在执行。
6.并行和并发
(1)并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
(2)并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面而不是真正的同时。并发往往在场景中有公有的资源,那么针对这个公有的资源往往产生瓶颈,我们会用TPS或者QPS来反映这个系统的处理能力。
二、Java多线程
1.Java给多线程编程提供了内置的支持。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
三、线程的生命周期:
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
线程的优先级
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
问:sleep()和wait()方法的区别?
1.sleep方法和wait方法都可以用来放弃cpu一定的时间,不同点在于如果线程持有某个对象的监视器(监视对象同步),sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器(锁,synchronized)。
2.wait()只能在同步(synchronized)中使用。
3.一个是Thread方法,一个是Object方法
线程安全:如果你的代码在多线程下执行和在单线程执行永远都能获得一样的结果,那么你的代码就是线程安全的。(加锁)
I.不可变:用final修饰的对象是线程安全的,值不可改变。
II.绝对线程安全:不管运行时环境如何,调用者都不需要额外的同步措施。Java中标注自己是线程安全的类,实际上绝大多数都不是线程安全的。绝对安全的类有:CopyOnWriteArrayList、CopyOnWriteArraySet。
III.相对线程安全:相对线程安全也就是我们通常意义上所说的线程安全,向vector这种,add、remove方法都是原子操作,不会被打断,但也仅限于此,如果有个线程在遍历某个vector、有个线程在add这个vector,会出现ConcurrentModificationException,也就是fail-fast机制。
IV.线程非安全:如ArrayList、LinkedList、HashMap等
线程死锁:任何多线程应用程序都有死锁风险。当一组进程或线程中的每一个都在等待一个只有该组中另一个进程才能引起的事件时,我们就说这组进程或线程死锁了。死锁最简单的情形是:线程A持有对象X的独占锁,并且在等待对象Y的锁,而线程B持有对象Y的独占锁,却在等待对象X的锁。
问:run()和start()之间的区别?
只有调用了start()方法,才会表现出多线程的特性,不同线程线程的run方法里面的代码交替执行。如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另一个线程才可以执行其run()方法里面的代码。
问:Runnable接口和Callable接口的区别?
Runnable接口中run()方法的返回值是void,他做的事只是纯粹的去执行run()方法中的代码而已;Callable接口中的call()方法时有返回值的,是一个泛型。和Future、FutureTask配合可以用来获取异步执行的结果。
package 多线程;
//线程类
public class ThreadA extends Thread{
//重写run()方法,线程执行方法
public void run(){//这个方法进行时,进入执行状态 系统自动调用
System.out.println("a");
}
}
------------------------------------------------
package 多线程;
public class ThreadB extends Thread {
public void run(){
System.out.println("b");
}
}
-------------------------------------------------
package 多线程;
public class Test {
//Main线程(主线程)
public static void main(String[] args) {
//新建了两个线程
Thread t1 = new ThreadA();
Thread t2 = new ThreadB();
//让线程进入就绪状态
t1.start();//3条线程 t1、t2、main
t2.start();
//t1.run();//1条线程 main
//t2.run();
//进入死亡状态
System.out.println("线程执行完毕");
}
}
out:
线程执行完毕
a
b
package 多线程2;
public class ThreadA extends Thread{
public void run(){
for (int i = 0; i < 11; i++) {
System.out.println("a"+i);
try{
//睡眠一会
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}
}
--------------------------------------------------------
package 多线程2;
public class ThreadB extends Thread{
public void run(){
for (int i = 0; i < 11; i++) {
System.out.println("b"+i);
try{
//睡眠一会
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}
}
--------------------------------------------------------------
package 多线程2;
public class Test {
public static void main(String[] args) {
//新建了两个线程
Thread t1 = new ThreadA();
Thread t2 = new ThreadB();
//让线程进入就绪状态
t1.start();//3条线程 t1、t2、main
t2.start();
//t1.run();//1条线程 main
//t2.run();
//进入死亡状态
//System.out.println("线程执行完毕");
}
}
/*
1.每一个应用程序至少有一条线程,Java中这个线程就是main线程
2.start()方法和run()方法区别:start()方法由程序员调用,说明这个线程就绪好了,而run()方法由系统调用,当cpu空闲时,执行的线程操作
3.线程的优先级跟cpu调度有关
*/
package 多线程3;
//通过实现Runnable接口来创建一个线程
//是个不完整的线程(没有start()方法)
public class ThreadA implements Runnable{
@Override
public void run() {
for (int i = 0; i < 11; i++) {
System.out.println("a"+i);
try{
//睡一会
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}
}
---------------------------------------------------------------
package 多线程3;
public class ThreadB implements Runnable{
public void run() {
for (int i = 0; i < 11; i++) {
System.out.println("b"+i);
try{
//睡一会
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}
}
-----------------------------------------------------------------
package 多线程3;
public class Test {
public static void main(String[] args) {
Runnable t1 = new ThreadA();
Runnable t2 = new ThreadB();
//转换
Thread td1 = new Thread(t1);
Thread td2 = new Thread(t2);
td1.start();
td2.start();
}
}
package 多线程4;
import java.util.concurrent.Callable;
public class ThreadA implements Callable {
//这个方法就是run()方法,只是多了一个返回值
@Override
public Object call() throws Exception {
for (int i = 0; i < 11; i++) {
System.out.println("a"+i);
try{
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
return null;
}
}
---------------------------------------------------------------------
package again;
import java.util.concurrent.Callable;
public class ThreadB implements Callable<String> {
public String call(){
for (int i = 0; i < 11; i++) {
System.out.println("b"+i);
try{
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
return null;
}
}
-----------------------------------------------------------------------
package 多线程4;
import jdk.nashorn.internal.codegen.CompilerConstants;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class Test {
public static void main(String[] args) throws Exception{
ThreadA t1 = new ThreadA();
ThreadB t2 = new ThreadB();
//将Callable---》FutureTask
FutureTask ft1 = new FutureTask(t1);
FutureTask ft2 = new FutureTask(t2);
//将FutureTask--》Thread
Thread d1 = new Thread(ft1);
Thread d2 = new Thread(ft2);
d1.start();
d2.start();
Object v1 = ft1.get();
Object v2 = ft2.get();
System.out.println(v1);
System.out.println(v2);
}
}
/*
1.继承Thread,重写run()方法,比较简单,就是一个线程类,直接可以调用start()方法;因为继承,局限性比较大
2.实现Runnable接口,它并不是一个直接的线程类,需要将它转换成一个线程类,推荐
3.实现Callable接口,它执行完call()操作后能够返回结果,启动这个线程比较麻烦,需要转换多次
*/
/*
yield和sleep
1.yield并不进入阻塞队列,直接就绪状态
2.sleep进入到阻塞状态,然后进入就绪状态
3.yield只让优先级比他高;sleep无所谓
4.yield扩展性比较差;sleep比较好
*/
package 守护线程;
//用户线程没完的话,程序继续执行
//否则结束
public class 守护线程 {
public static void main(String[] args) {
DemoB1 b1 = new DemoB1();
DemoB2 b2 = new DemoB2();
b2.setDaemon(true);//设置b2位守护线程|后台线程
b1.start();
b2.start();
}
}
class DemoB1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("b1:"+i);
try{
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}
}
class DemoB2 extends Thread{
@Override
public void run() {
while(true){
System.out.println("b2");
try{
Thread.sleep(1000);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}
}
package 定时器;
import java.util.Timer;
import java.util.TimerTask;
public class 定时器 {
public static void main(String[] args) {
TimerTask tt = new 定时();//定时器 由 Timer 安排为一次执行或重复执行的任务。
Timer timer = new Timer();//一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行
timer.schedule(tt,1000,4000);//schedule(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定延迟执行。
//timer.schedule(tt,1000);//schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务。
}
}
//这个类就是定时器类
class 定时 extends TimerTask{
public void run(){
System.out.println("你好!");
}
}