java——线程(一)

      说实话,从大一开始学习java之后,到现在大三即将面试进行实习,这期间,我还真没有怎样使用过线程技术,即使做项目的时候,我也接触到线程的机会也是极其少的,我将原因归结于,水平太低,还没有达到高层次的开发,毕竟没有做过正规项目的开发。这一部分知识,早就忘得一干二净了,仅仅知道线程的创建方式有三种,等基本的用法,让我将线程用于开发中,我怕挨揍........

        那个,这里提一句,如果有同学需要学习资源的(主要是视频),可以将加的QQ:987097545,这些视频,都是我这几年学习时收集的,可以分享哦!

     这一篇线程,涉及的都是比较基础的线程知识点。

     使用多线程,并不能提高效率。。。。

      只有实现Runable接口的类的对象,才可以称为线程。Runable接口只有一个方法,就是run()方法。

      我们都只到创建多线程的方式有三种,一般常见的是两种,

             一种就是继承Thread类,并重写run()方法,因为Thread类实现了Runable接口

             另一种就是直接实现Runable接口。

//方式一:
        new Thread(){
		public void run(){
			//自己的业务代码	
			}
		}.start();//相当于创建的是一个Thread的子类对象,重写了run()方法

//方式二:		
	 new Thread(new Runnable(){
			public void run(){
			   //自己的业务代码	
			}			
		}).start();

        通常使用第二种方式,因为接口可以避免单继承的限制。

       我们来看一下两种方式实现的效果。

       方式一:

public class ThreadDemo extends Thread{
	private int i;
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println(i+"-----"+Thread.currentThread().getName());
		}
	}
	public static void main(String[] args) {
		new ThreadDemo().start();
		new ThreadDemo().start();
	}
}


   会发现,出现了两个线程,变量i的值有重复的,证明两个线程无法共享变量i。

方式二

public class RunableDemo implements Runnable{

	private int i;
	@Override
	public void run() {
		for(;i<100;i++){
			System.out.println(i+"-----"+Thread.currentThread().getName());
		}
	}
	public static void main(String[] args) {
		RunableDemo runableDemo=new RunableDemo();
		new Thread(runableDemo,"线程一").start();//传递的是同一个Runnable类型的对象
		new Thread(runableDemo,"线程222222222").start();
	}
}

    此时,两个线程共享变量i,为什么呢?原因就是Thread类的构造方法,内部调用了init()方法,init方法会将该runnable的类的引用传给Thread类的成员变量target。所以多个对象可以共享变量

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

   但是,如果传递的不是同一个Runnable类的对象,还是不能共享的。共享情况只出现在多个Thread对象使用了同一个Runnable类的对象,此时,多个Thread对象可以共享变量。共享变量,还可以使用static关键字。

   

  还有一个简单的问题,run()和start()方法的区别是什么?有时候,我们使用时,就会冒出这样一个想法,他们两个有什么区别?

    run()仅仅是封装被线程执行的代码,没有什么特殊的含义,就是一个普通方法,被调用。

    start(),启动该线程,然后又jvm去掉用run()方法。

 

      jvm虚拟机的启动是一个多线程的,有main线程的启动,垃圾回收机制线程的启动等,所以是一个多线程的。

      线程的创建方式,我们已经介绍完了,对,还有一种方式,之后再介绍。

      接下来就是线程常用的api介绍了。

     ——获取线程的信息

          获取线程,就要先获取当前线程。

static ThreadcurrentThread()
          返回对当前正在执行的线程对象的引用。
         附上这个方法的源码,是一个本地方法。
public static native Thread currentThread();

        常用的方法。

longgetId()
          返回该线程的标识符。
 StringgetName()
          返回该线程的名称。
 intgetPriority()
          返回线程的优先级。
 Thread.StategetState()
          返回该线程的状态。
 ThreadGroupgetThreadGroup()
          返回该线程所属的线程组。

         ——设置线程的优先级

 voidsetPriority(int newPriority)
          更改线程的优先级。
线程优先级有三种,这是Thread中的源码。静态的,可以通过类名.方式直接调用。
    public final static int MIN_PRIORITY = 1;//优先级最低
    public final static int NORM_PRIORITY = 5;//默认优先级
    public final static int MAX_PRIORITY = 10;//优先级最高

       ——线程休眠

   //指定的毫秒数内让正在执行的线程休眠
    public static native void sleep(long millis) throws InterruptedException;
    
    //指定的毫秒数+纳秒数内让正在执行的线程休眠
    public static void sleep(long millis, int nanos)
    //millis毫秒数,nanos纳秒数,1毫秒(ms)=1000000纳秒(ns)= 一百万纳秒
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }
        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            //指定的纳秒数大于二分之一的毫秒数,或者millis为0,nanos不为0
            millis++;
        }
        sleep(millis);//还是调用的是本地的sleep()法
    }

       注意:

         1. 线程休眠,程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。是就绪状态。

          2.不会释放对象锁,与wait()不同,之后会讲。

          3.sleep()方法是一个静态方法,通过Thread.sleep()方法调用,但是,此时休眠的线程时主线程main,所以必须在run()方法内,使用sleep()方法。jvm-->run()-->sleep(),线程休眠

          4.休眠线程,被中断会抛出异常,中断线程可以使用interrupt()方法。调用interrupt方法,会抛出一个异常。

      之后,我测了一个测试,测试一下interrupt()方法。

public class RunableDemo implements Runnable{

	private int i;
	@Override
	public void run() {
		for(;i<100;i++){
			System.out.println(i+"-----"+Thread.currentThread().getName());
			try {
				Thread.sleep(1000);//没啥意思,只是显示的慢一点
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) {
		RunableDemo runableDemo=new RunableDemo();
		Thread th=new Thread(runableDemo,"线程一");
		th.start();
		th.interrupt();
	}
}

       看一下效果图。

   md,啥意思,不是线程被中断了吗,怎么还会继续呢?

   就是说,interrupt()方法的中断,只是声明警告,会给这个线程设置一个为true的变量的中断标志(Boolean类型的变量),如果当前线程的状态为非阻塞状态,仅仅是修改变量值而已,当线程的状态为阻塞状态,会出现三种情况 

     1.sleep(),wait(),jion引起的阻塞,会重新设置变量值为fasle,并抛出一个异常,就是说调用了interrupt()方法之后,再次调用上面三种方法,中断标志由true变为false。

     2.如果是java.nio.channels.InterruptibleChannel进行的io操作引起的阻塞,则会对线程抛出一个ClosedByInterruptedException;(待验证)

     3.如果是轮询(java.nio.channels.Selectors)引起的线程阻塞,则立即返回,不会抛出异常。(待验证)

 Thread.interrupted()方法,也会使线程状态复位,变为false。

 在Core Java中有这样一句话:”没有任何语言方面的需求要求一个被中断的程序应该终止。中断一个线程只是为了引起该线程的注意,被中断线程可以决定如何应对中断 “。

public class RunableDemo implements Runnable{

	private int i;
	@Override
	public void run() {
		for(;i<100;i++){
			System.out.println(i+"-----"+Thread.currentThread().getName()+"-->"+Thread.currentThread().isInterrupted());
			
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Thread.currentThread().interrupt();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().isInterrupted());
			if(i==50){
				Thread.currentThread().interrupted();
			}
		}
	}
	public static void main(String[] args) {
		RunableDemo runableDemo=new RunableDemo();
		Thread th=new Thread(runableDemo,"线程一");
		th.start();
		//th.interrupt();
	}

         ——线程加入

 可以使用join()方法,强制让该线程停下来,等待另一个线程的执行


 join方法,没有指定时间,会等待另一个线程直线完毕后,有阻塞变为就绪,

                指定了时间,如果指定的时间内,另一个线程没有执行完毕,该线程也会有阻塞变为就绪

               指定了时间,指定了时间,时间内,另一个线程足以执行完毕,当这个线程执行完毕后,就该线程就有阻塞变为就绪

 

      ——线程唤醒

   使用的就是interrupted()方法。

      ——后台线程(为其他线程服务,仅当所有的非后台线程都结束时,后台线程才会结束)

     将线程设置为后台线程,必须在线程启动之前,调用setDaemon()设置为后台。main线程就是一个后台线程。

     ——线程礼让(run()内)

      使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。

用了yield方法后,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)

yield()和sleep()还是有区别的。

       1.yield()方法,会将运行权让给具有相同或更高的优先级的线程,sleep()方法不考虑优先级

       2.yield()方法,使线程有运行--》就绪,sleep()方法使线程由运行--》阻塞

      3.yield()方法不抛任何异常,sleep()可以抛出InterruptedException

      4.sleep()比yield()具有更好的移植性,不能依靠yield来提高程序的并发性。


      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值