Java多线程编程核心技术(第三版)

1.进程|线程

进程是系统分配和管理的资源的基本单位。

进程就是在系统中运行的程序或者任务,比如在任务管理器中,你看到的每一个正在运行的exe程序就可以理解成
一个进程。

--------------------------------
在IDE中运行*.class文件相当于在操作系统中启动一个JVM虚拟机进程,在该虚拟机进程加载*.class文件并运
行。

--------------------------------
线程是在进程中独立运行的子任务。


进程和线程总结:
1.进程虽然是相互独立的,但它们可以互相通信,通用的方式是使用Socket或HTTP协议。
2.进程拥有共享的系统资源,比如内存、网络端口、供其内部线程使用。
3.进程较重、因为创建进程需要操作系统分配资源,会占用内存。
4.线程存在于进程中,是进程的一个子集,先有进程、后有线程。
5.虽然线程更轻,但线程上下文切换的时间成本非常高。

2.使用多线程的优点

充分利用CPU的空闲时间来处理其他任务,由于任务的切换速度很快,给使用者的感受就是这些任务同时运行,所
以使用多线程技术可以在同一时间内做更多不同种类的任务。


---------------------------------
补充:
1.单任务的特点就是排队执行,也就是同步。在同一时间只能执行一个任务,CPU利用率大幅降低,这就是单任务环境的缺点。
2.多任务的特点是在同一时间内可以执行多个任务,使用多线程就是在使用异步。

3.在什么场景使用多线程

1.阻塞:系统中出现了阻塞现象,则可以根据实际情况来使用多线程提高运行效率。
2.依赖:业务A阻塞时,业务B不依赖业务A的执行结果时,可以用多线程提高运行效率。

4.继承Thread类

Java的JDK开发包自带了对多线程技术的支持,可以方便地进行多线程编程。实现多线程编程的方式主要有两种:
一种是继承Thread类,另外一种是实现Runnable接口。这两种方式创建线程的功能都是一样的,没有本质的区
别。只不过继承的局限就是单继承,实现可以多实现。
public class ThreadTest {
    public static void main(String[] args) {

        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println("运行结束!");
    }
}
------------------------------------
public class MyThread extends Thread{

    @Override
    public void run() {
        System.out.println("MyThread");
    }
}
------------------------------------
输出:
运行结束!
MyThread

Process finished with exit code 0

代码使用start()方法来启动一个线程,线程启动后会自动调用线程对象中的run()方法,run()方法里面的代码
就是线程对象要执行的任务,是线程执行任务的入口。

注意:
使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序无关。因为线程是一个子任务,CPU是以不确定的
方式,或者随机的时间来调用线程中的run()方法,所以先输出"运行结束!"和先输出"MyThread"具有不确定性。

5.常见的3个命令分析线程的信息

如果想要查看线程的状态与信息,则可采用3种常见的命令,分别是jsp+jstack.exe、jms.exe和jvisualvm.exe
它们在jdk\bin文件夹中。
1.jps+jstack.exe命令,在cmd中输入jps命令查看Java进程,然后使用jstack命令查看该进程下线程的状态:

C:\Program Files\Java\jdk1.8.0_201\bin>jps  --直接回车 便可以查询进程对应的PID
7088 Launcher
7712 ThreadTest2
4276 Jps
7096
11564 RemoteMavenServer36

C:\Program Files\Java\jdk1.8.0_201\bin>jstack -l 7712  --回车就可以看到线程的状态
2.使用jmc.exe命令
在jdk/bin中双击jmc.exe命令

注意:如果在jmc.exe中看不到JVM进程,说明jmc.exe和IDE中使用的JDK版本不一致。
3.使用jvisualvm.exe命令,双击jvisualvm.exe命令。

6.执行start()的顺序不代表执行run()的顺序

执行start()方法的顺序不代表线程启动的顺序,不代表run()方法执行顺序,执行run()是随机的。



public class ThreadTest3 extends Thread {

    private int i = 0;

    public ThreadTest3(int i) {
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "---->i" + i);
    }
}



public class ThreadTest {
    public static void main(String[] args) {

        ThreadTest3 t1 = new ThreadTest3(1);
        ThreadTest3 t2 = new ThreadTest3(2);
        ThreadTest3 t3 = new ThreadTest3(3);
        ThreadTest3 t4 = new ThreadTest3(4);
        ThreadTest3 t5 = new ThreadTest3(5);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

-------------------------------------------
Thread-2---->i3
Thread-3---->i4
Thread-1---->i2
Thread-4---->i5
Thread-0---->i1

Process finished with exit code 0

7.public Thread(Runable target)中的target参数

JVM直接调用的是Thread.java类中的run()方法。该方法源代码如下:

@Override
public void run() {
	if (target != null) {
		target.run();
	}
}

/* What will be run. */
private Runnable target;
变量target是声明的Runnable对象,结合if判断就可以执行Runnable 对象的run()方法了。变量target是在
init()方法中进行赋值初始化的。init方法又在构造器中被调用了。
-------------构造----------------------
public Thread(Runnable target) {
	init(null, target, "Thread-" + nextThreadNum(), 0);
}

------------init-------------------------
private void init(ThreadGroup g, Runnable target, String name,
				  long stackSize) {
	init(g, target, name, stackSize, null, true);
}

8.线程安全问题

出现线程安全情况:多个线程操作同一个对象的同一个实例变量。


public class MyThread extends Thread{

   private Integer count = 5;

    @Override
    public void run() {
        super.run();
        count--;
        System.out.println("线程名称:"+MyThread.currentThread().getName()+",count值="+count);
    }


    public static void main(String[] args) {

        MyThread myThread = new MyThread();


        //多个线程操作同一个对象的同一个实例属性,会出现线程安全问题
        Thread thread1 = new Thread(myThread);
        Thread thread2 = new Thread(myThread);
        Thread thread3 = new Thread(myThread);
        Thread thread4 = new Thread(myThread);
        Thread thread5 = new Thread(myThread);

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();

    }
}


输出结果:

线程名称:Thread-3,count值=3
线程名称:Thread-1,count值=2
线程名称:Thread-4,count值=2
线程名称:Thread-2,count值=1
线程名称:Thread-5,count值=0

Process finished with exit code 0



count--的操作要分解成3步,执行这3步骤的过程会被其他线程所打断,因为不是"原子性"的操作。
(1):取得原有count值
(2):计算count-1
(3):对count进行重新赋值
在这3个步骤中,如果有多个线程同时访问的话,大概率会出现线程安全问题。

9.synchronized关键字

public class MyThread extends Thread{

   private Integer count = 5;

    @Override
    public synchronized void run() {
        super.run();
        count--;
        System.out.println("线程名称:"+MyThread.currentThread().getName()+",count值="+count);
    }


    public static void main(String[] args) {

        MyThread myThread = new MyThread();


        //多个线程操作同一个对象的同一个实例属性,会出现线程安全问题
        Thread thread1 = new Thread(myThread);
        Thread thread2 = new Thread(myThread);
        Thread thread3 = new Thread(myThread);
        Thread thread4 = new Thread(myThread);
        Thread thread5 = new Thread(myThread);

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();

    }
}

输出结果:

线程名称:Thread-1,count值=4
线程名称:Thread-5,count值=3
线程名称:Thread-4,count值=2
线程名称:Thread-3,count值=1
线程名称:Thread-2,count值=0

Process finished with exit code 0

加上synchronized关键字,来解决线程安全问题。一个线程在调用run方法时,会判断run方法有没有上锁,
如果上锁,说明其他线程正在调用run方法,必须等其他线程调用结束后,才可以执行run方法。虽然count-- 
操作,仍被划分3个步骤,但是在执行这3个步骤时并没有被打断,呈“原子性”,所以运行结果是正确的。

使用synchronized关键字修饰的方法称为“同步方法”,可用来对方法内部的全部代码进行加锁,而加锁的这
段代码称为“互斥区”或“临界区”。

当一个线程想要执行同步方法里面的代码时,它会首先尝试去拿这把锁,如果能拿到,那么该线程就会执行
synchronized里面的代码。如果不能拿到,那么这个线程就会不断尝试去拿这把锁,直到拿到为止。

10.isAlive()

isAlive()的作用是测试线程是否处于活动状态。即线程已经启动尚未终止。如果线程处于正在运行或者准备开
始运行的状态,就认为线程是“存活”的。


public class MyThread extends Thread{

    @Override
    public void run() {
        super.run();
        System.out.println("线程名称:"+this.currentThread().getName()+",是否存活:"+this.isAlive());
    }


    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        System.out.println("线程名称:"+myThread.getName()+",是否存活:"+myThread.isAlive());
        myThread.start();
        Thread.sleep(1000L);
        System.out.println("线程名称:"+myThread.getName()+",是否存活:"+myThread.isAlive());
    }
}

线程名称:Thread-0,是否存活:false
线程名称:Thread-0,是否存活:true
线程名称:Thread-0,是否存活:false

Process finished with exit code 0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值