Java再学进程の并发基础

线程的基本概念

1、创建线程

线程表示一条单独的执行流,他有自己的程序执行计数器,有自己的栈
创建线程的两种方式:继承Thread和实现Runnable接口
【1】继承Thead
Java中java.lang.Thread这个类表示线程,一个类可以继承Thread重写run方法来实现一个线程

package lianxi.xiancheng;

import  java.lang.Thread;
public class HelloThread extends Thread{
    @Override
    //run方法的方法签名是固定的,public,没有参数,没有返回值,不能抛出受检异常。
    //run方法类似与单线程中的main方法,线程从run方法的第一条语句开始执行知道结束
    public void run(){
        System.out.println("hello");
    }
}

这是定义的一个类继承了Thead类,重写了run方法,但是代码并不会被执行,想要启动线程还需要创建HelloThread对象,然后调用Thread的start方法

    public static void main(String[] args) {
        HelloThread h = new HelloThread();
        Thread t = new HelloThread();
        h.start();
        t.start();
    }
'输出结果:'
hello
hello

为什么调用start(),执行的却是run方法呢?
start表示启动该线程,使其成为一条单独的执行流,操作系统会分配线程相关的资源,每个线程会有单独的程序执行计数器和栈,操作系统会把线程作为一个独立的个体进行调度,分配时间片让它执行,执行的起点就是run方法

如果不调用start(),直接调用run方法呢?
屏幕输出并不会发生变化,但并不会启动一条单独的执行流,run方法只是main线程中的一个普通方法罢了。

如何判断当前代码在哪个线程中执行的呢?
Thread有一个静态方法,currentThread,返回当前执行的线程对象:

public static native Thread currentThread();

每个Thread都有一个idname:

public long getId()
public final String getName()

如:

System.out.println(t.getId()+"  "+t.getName());
System.out.println(Thread.currentThread().getId()+" "+Thread.currentThread().getName());

'输出:' 
15  Thread-1//h.start(); t.start();因此t线程的名字是Thread-1,h.start();的线程名字是Thread-0
1 main

当调用了h.start();之后,就有了两条执行流,新的一条执行run方法,旧的一条继续执行main方法,两条执行流并发执行,操作系统负责调度。单cup上一次只能有一个线程执行,多cpu同时刻可以执行多条,但是操作系统给我们屏蔽了这种差异,给程序员的感觉就是多个线程在并发执行芒担石哪条语句先执行哪条语句后执行是不一定的。当所有线程都执行完毕的时候,程序退出。


【2】实现Runnable接口

通过继承Thread来实现线程虽然比较简单**,但是Java中只支持单继承,每个类最多只有一个父类,如果类已经有父类了,就不能再继承Thread**,这时可以通过java.lang.Runnable接口来实现线程。

Runnable接口的定义只有一个run方法:

public interface Runnable{
	public abstract void run();
}

一个类可以实现该接口,并实现run方法,如下:

package lianxi.xiancheng;

public class HelloRunnable implements Runnable{
    @Override
    public void run(){
        System.out.println("hello");
    }
}

但是仅仅实现Runnable 是不够的,要启动线程,还需要创建一个Thread对象,但传递一个Runnable 对象,如下:

    public static void main(String[] args) {
        Thread helloThead = new Thread(new HelloRunnable());
        helloThead.start();
    }

无论是通过Thread还是实现 Runnable接口来创建线程,启动线程都是调用start方法。

2、线程的基本属性和方法

线程有一些基本属性和方法,包括id,name,优先级,状态,是否damon线程,sleep方法,yield方法,join方法,过时方法等

【1】id和name

每一个线程都有一个id和name。id是一个递增的整数,每创建一个线程就+1.name的默认值是Thread-后跟一个编号。name可以在Thread 的构造方法中指定也可以用setName方法进行设置。

    public static void main(String[] args) {
        Thread helloThead = new Thread(new HelloRunnable());
        helloThead.setName("helloRunnable");
        System.out.println(helloThead.getName());
    }
'输出:'helloRunnable

【2】优先级
线程有一个优先级的概念,在Java中,优先级从1到10,默认是5,相关方法为:

public final void setPriority(int newPriority)
public final int getPriority()

这个优先级会被映射到操作系统中线程的优先级,不过因为操作系统各不相同,不一定都是10个优先级,Java中不同优先级可能会被映射到操作系统中相同的优先级。另外,优先级对操作系统而主要是一种建议和提示,而非强制。简单地说,在编程中,不要过于依赖优先级。

【3】状态

线程有状态的概念,获取线程概念的方法为:

public State getState()

返回值类型为Thread.State,他是一个枚举类型,如下值:

public enum State{
	NEW,
	RUNNABLE,
	BLOCKED,
	WAITING,
	TIMED_WAITING,
	TERMINATED;
}

这些状态的解释:
NEW:没有调用start 的线程状态为NEW.
TERMINATED:线程运行结束后状态为TERMINATED
RUNNABLE:调用start后在执行run方法且没有阻塞时状态为RUNNABLE,不过RUNNABLE不代表CPU一定在执行该线程的代码,可能正在执行也可能正在等待系统分配时间片,只是没有在等待的其他条件
BLOCKED、WAITING、TIME_WAITING:都表示线程被阻塞了。在等待一些条件。区别见后!!

    public static void main(String[] args) {
        Thread helloThead = new Thread(new HelloRunnable());
        helloThead.setName("helloRunnable");
        System.out.println(helloThead.getState());
        helloThead.start();
        System.out.println(helloThead.getState());
        try {
            sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(helloThead.getState());
    }
'输出为:'
NEW
RUNNABLE
hello
TERMINATED

Thread还有一个方法,返回线程是否活着:

public final native boolean isAlive()

线程被启动后,run方法运行结束前,返回值都是true.

【4】是否为daemon线程
Thread 有一个是否daemon线程的属性,相关方法为:

public final void setDaemon(boolean on)
public final boolean isDaemon()

启动线程会启动一个单独的执行流,整个程序只有在所有线程都结束的时候才退出,但daemon 线程是个例外,当整个程序中剩下的都是daemon 线程的时候,程序才会退出

daemon线程的作用?
它一般是其他线程的辅助线程,在他辅助的主线程退出的时候,她就没有存在的意义了。

【5】sleep方法
Thread有一个静态的sleep方法,调用该方法会让当前线程睡眠指定的时间,单位是毫秒:

public static native void sleep(long millis) throws InterruptedException

睡眠期间,该线程为让出CPU,但睡眠的时间不一定是确切的给定毫秒数,可能有一定的偏差,偏差与系统定时器和操作系统调度的准确度和精度有关,。睡眠期间,线程可以被中断,如果被中断,sleep会抛出InterruptedException。

【6】yield

Thread还有一个让出CPU的方法:

public static native void yield()

这是一个静态方法,调用该方法,是告诉操作系统的调度器:我现在不着急占用CPU,你可以先让其他线程运行。但这仅仅建议,调度器如何处理是不一定的,她可能完全忽略此调用。

【7】join方法
在上面例子中,helloThread还没执行完,main线程可能就执行完了,Thread有一个join方法,可以让调用join的线程等待该线程结束,join方法的声明为:

public final void join() throws InterruptedException

在等待该线程结束的过程中,这个等待可能被中断,如果被中断,会抛出InterruptedException。

join方还有一个变体,可以限定等待的最长时间,单位为毫秒,如果为0,表示无限期等待:

public final synchronized void join(long millis) throws InterruptedException

在前面的HelloThread中,如果希望main线程在子程序结束后再退出,main方法可以改为:

public  static void main(String[] args) throws InterruptedException{
 Thread  thread = new HelloThread();
 thread.start();
 thread.join();
}

【8】过时方法
Thread类中还有一些看上去可以控制线程周期的方法:

public final void stop()
public final void suspend()
public final void resume()

这些方法因为各种原因已经被标记为过时。

3、共享内存及可能出现的问题

每个线程表示一条单独的 流,有自己对程序计数器,有自己的栈,但是线程之间可以共享内存,他们可以访问和操作相同的对象。
未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值