Java学习笔记之多线程(一)

这周的事情有点多了,所以学习进度有点慢。。

并行和并发

  • 并行指应用能够同时执行不同的任务,并发指应用能够交替执行不同的任务。
  • 并行是指同一时刻同时做多件事情,并发是指同一时间间隔内做多件事情。
  • 并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
  • 在一台处理器上“同时”处理多个任务是并行,在多台处理器上同时处理多个任务是并发。

进程

直译:正在进行中的程序;
进程是不直接执行的,它只是在分配该应用程序的内存空间。
一个进程中,不可能没有线程。

线程

就是进程中一个负责程序执行的控制单元(执行路径)。
负责进程中内容执行的一个控制单元,也称之为执行路径,也称之为执行情景。

多线程

一个进程中,可以多执行路径,称之为多线程。

开启多个线程,是为了同时执行多部分代码。
每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。

那为什么要用多线程呢?

  1. 使用线程可以把占据长时间的程序中的任务放到后台去处理
  2. 用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度
  3. 程序的运行速度可能加快

开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力以及性能。

多线程的弊端

看上去是多线程同时执行,其实,在某一时刻,只有一个线程在执行,只是切换速度很快。
CPU的切换是随机的。
开的线程多了会造成卡顿,甚至死机,导致效率的降低。

进程与线程的关系

  1. 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。
  2. 资源分配给进程,同一进程的所有线程共享该进程的所有资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。
  3. 处理机分给线程,即真正在处理机上运行的是线程。
  4. 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

JVM中的多线程解析

虚拟机的启动本身就依赖了多条线程。
至少有两个线程可以分析出来;
1.主线程
该线程的任务代码都定义在主函数中。
2.垃圾回收线程。
该线程的任务代码都在垃圾回收器定义。

例:

class Demo
{
	
}

class ThreadDemo
{
	public static void main(String[] args)
	{
		new Demo();
		new Demo();
		new Demo();
		System.out.println("Hello world");
	}
}

java虚拟机程序在运行的时候,肯定有一条线程是在执行主函数的(主线程),还有回收垃圾的线程(垃圾回收线程)等。

class Demo extends Object
{
	public void finalize()
	{
		System.out.println("Demo ok");
	}
}

class ThreadDemo
{
	public static void main(String[] args)
	{
		new Demo();
		new Demo();
		new Demo();
		System.out.println("Hello world");
	}
}

运行结果:

Hello world

但是,如果运行垃圾回收器(System类gc()方法)
则:

class Demo extends Object
{
	public void finalize()
	{
		System.out.println("Demo ok");
	}
}

class ThreadDemo
{
	public static void main(String[] args)
	{
		new Demo();
		new Demo();
		System.gc();
		new Demo();
		System.out.println("Hello world");
	}
}

注意:gc();不是立刻就让垃圾回收器启动,只是在告诉垃圾回收器需要启动。
运行结果就成为了:

Hello world
Demo ok
Demo ok

原因:
因为该程序是两个线程完成的,先运行主线程,JVM关闭前运行垃圾回收线程。
也有可能出现其他情况:
例如:

Hello world
Demo ok

或是:

Demo ok
Hello world

原因:虚拟机准备结束的时候,会强制清除它所在的内存区域。

主线程运行

主方法单线程运行

class Demo 
{
    private String name;
    Demo (Srting name) 
    {
        this.name = name;
    }
    public void show() 
    {
        for(int x = 0; x < 10; x++) 
        {
           for(int y =9999999;y<999999999;y++)
           {

			}
           System.out.println(name+"...x="+x);
        }
    }
}
class ThreadDemo2
{
    public static void main(String[] args) 
    {
        Demo d1 = new Demo("一乘一");
        Demo d2 = new Demo("liuxilin");
        d1.show();
        d2.show();
    }
}

运行结果:

一乘一…x=0
一乘一…x=1
一乘一…x=2
一乘一…x=3
一乘一…x=4
一乘一…x=5
一乘一…x=6
一乘一…x=7
一乘一…x=8
一乘一…x=9
liuxilin…x=0
liuxilin…x=1
liuxilin…x=2
liuxilin…x=3
liuxilin…x=4
liuxilin…x=5
liuxilin…x=6
liuxilin…x=7
liuxilin…x=8
liuxilin…x=9

y的for循环让每次输出都有一定的延迟,但是必须d1都输出完才输出d2,这时就需要多线程来让他们同时输出
那么,该如何创建一个路径呢?

线程的实现

在Java中实现多线程有两种手段,一种是继承Thread类,另一种就是实现Runnable接口。

继承Thread类

  1. 定义一个类,继承Thread类
  2. 重写Thread类中的 run(); 方法
  3. 直接创建Thread类的子类对象(创建线程)
  4. 调用 start(); 方法开启线程,并调用线程的任务run(); 方法
class Demo extends Thread
{
    private String name;
    Demo (Srting name) 
    {
        this.name = name;
    }
    public void run() 
    {
        for(int x = 0; x < 10; x++) 
        {
           //currentThread()获取当前运行中线程的引用
           System.out.println(name+"...x="+x+"...Thread Name="+Thread.currentThread().getname());
        }
    }
}
class ThreadDemo 
{
    public static void main(String[] args) 
    {
        Demo d1 = new Demo("一乘一");
        Demo d2 = new Demo("liuxilin");
        d1.start();//开启线程,调用run方法
        d2.start();//开启线程,调用run方法
        //CPU在主线程,d1,d2之间随机高速切换
    }
}

实现Runnable接口

  1. 定义一个类,实现Runnable接口
  2. 重写接口中的run方法,将线程的任务代码封装到run方法中
  3. 通过Thread类创建线程对象,将Runnable接口的子类对象作为构造方法的参数进行传递
  4. 调用线程对象的start(); 方法开启线程
class Demo extends Fu implements Runable
{ 	//无法继承Thread但是需要多线程,通过接口形式完成
    public void run() 
    {
        show();
    }
    public void show() 
    {
        for(int x = 0; x < 20; x++) 
        {
            System.out.println(Thread.currentThread().getName()+"..."+x);
        }
    }
}
class ThreadDemo 
{
    public static void main(String[] args) 
    {
        Demo d = new Demo();
        Thread t1 = new Thread(d);
        Thread t2 = new Thread(d);
        t1.start;
        t2.start;
    }
}

从程序可以看出,现在的两个线程对象是交错运行的,哪个线程对象抢到了CPU资源,哪个线程就可以运行,所以程序每次的运行结果肯定是不一样的,在线程启动虽然调用的是start()方法,但实际上调用的却是run()方法定义的主体。

第二类方法的好处

  • 从Thread类的定义可以清楚的发现,Thread类也是Runnable接口的子类,但在Thread类中并没有完全实现Runnable接口中的run()方法;
  • 将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象;
  • 避免了java单继承的局限性。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值