java多线程之基础(一)

线程和进程:
进程:资源(cpu,内存,磁盘)分配的最小单位
线程:归属于某个进程,cpu调度的最小单元(栈、程序计数器),堆

cpu轮转机制(RR调度):主要用于分时系统中的进程调度。为了实现轮转调度,系统把所有就绪进程按先入先出的原则排成一个队列。新来的进程加到就绪队列末尾。每当执行进程调度时,进程调度程序总是选出就绪队列的队首进程,让它在CPU上运行一个时间片的时间。时间片是一个小的时间单位,通常为10~100ms数量级。当进程用完分给它的时间片后,系统的计时器发出时钟中断,调度程序便停止该进程的运行,把它放入就绪队列的末尾;然后,把CPU分给就绪队列的队首进程,同样也让它运行一个时间片,如此往

并行和并发: 并行可以理解成并排而行,同时运行(一个任务切分成多个子任务,在多个CPU上同时运行)。而并发则离不开时间单位,指多个任务交替使用CPU,同一时刻还是只有一个任务在跑。

下面就代码示例讨论一些问题

1、先创建用两种方式创建一个线程

public static class testThread extends  Thread{//继承Thread类创建线程
        @Override
        public void run() {
            System.out.println("testThread :"+Thread.currentThread().getName());
        }
    }
    public static class testRunable implements Runnable{//实现Runnable接口创建线程
        @Override
        public void run() {
            System.out.println("testRunable :"+Thread.currentThread().getName());
        }
    }

Runable和Thread的区别

1、Runable是对任务(业务逻辑)的抽象,而Thead才是对线程的抽象。
2、Thread是基类,子类必继承他实现其run方法。其也是实现了Runable接口。Thread是普通的类,并非抽象类或者密封类等。
3、Runnable是接口,子类必须实现run方法,该接口就只有唯一的抽象方法run。
由于Java单继承,所以Thead通过类继承方式实现接口,存在扩展性问题。
4、他们都是通过start方法来启动,可以达到异步操作,如果用run方法启动其将是同步方法,失去多线程的意义。

testThread  t1 = new testThread();
testThread  t2 = new testThread();
t1.start();
t2.start();         //这种方式是两个线程处理两个任务,各自变量独立!
testRunable  r1 = new testRunable();//实现了runable接口
Thread tr1 = new Thread(r1);
 Thread tr2 = new Thread(r1);
 tr1.start();
tr2.start();      //这种是两个线程处理一个任务,共享资源!

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。
我们先试着写这样一个main方法

public static void main(String[] args) {
        TestThread thread = new TestThread();
        thread.start();
        thread.start(); //报错
   TestRunable thread1 = new TestRunable();
    new Thread(thread1).start();//此时执行run方法的线程是thread1
    thread1.run();//此时执行run方法的线程是main线程
         }

猜猜会不会报错,为什么呢?
java.lang.IllegalThreadStateException;这就牵扯到接下来要将的线程五大状态了。
在这里插入图片描述
1、新建状态
当new一个线程实例的时候,这个线程就是新建状态。(此时还只是jvm中的一个类,与资源分配无关)
2、就绪状态
当这个类调用了start(),就已经进入就绪状态,随时等待CPU分配时间片,变成运行状态。一个进程可以有很多线程进入就绪状态。
3、运行状态
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法。当时间片结束后又变回到线程队伍尾端,变成就绪状态,等待下一次时间片分配。
4、阻塞状态
线程运行过程中,可能由于各种原因进入阻塞状态:
①线程通过调用sleep方法进入睡眠状态;
②线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
③线程试图得到一个锁,而该锁正被其他线程持有;
④线程在等待某个触发条件;
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
5.死亡状态(dead)
①线程运行完成或者遇到异常而自然死亡;

②一个未捕获的异常终止了run方法而使线程猝死;

为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法,如果是可运行或被阻塞,这个方法返回true;如果线程仍旧是new状态且不是可运行的,或者线程死亡了,则返回false。

释放锁的几种情况:
Java 中多线程锁释放的条件:
1、执行完同步代码块,就会释放锁(synchronized)
2、在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放(exception)
3、在执行同步代码块的过程中,执行了锁所属对象的 wait() 方法,这个线程会释放锁,进入对象的等待池(wait)

Start和run的区别
直接运行run方法就是普通对象的普通方法,和别的类直接定义的Run方法没区别。只有调用了start()后,Java才会将线程对象和操作系统中实际的线程进行映射,再来执行run方法(此时才是新起的线程调用的,否则是main线程调用)。

Thread.setPriority(1) 设置线程优先级,取值为1~10,缺省为5,但线程的优先级不可靠,不建议作为线程开发时候的手段

守护线程:和主线程(未必共生)共死,且线程中的finally语句不能保证一定执行

  Thread rn = new TestThread();
    rn.setDaemon(true);//设置为守护线程,一定要在start之前
    rn.start();
    rn.sleep(1);

终止一个线程

java线程是协作式,而非抢占式

调用一个线程的interrupt() 方法中断一个线程,并不是强行关闭这个线程,只是跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。

stop:这个方法实际上已经被弃用了,因为该方法是不安全的,
stop方法弃用原因?
调用 stop() 方法会立刻停止 run() 方法中剩余的全部工作,包括在 catch 或 finally 语句中的,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。
调用 stop() 方法会立即释放该线程所持有的所有的锁,导致数据得不到同步,出现数据不一致的问题。

使用标志位isInterrupted()关闭锁:
isInterrupted()默认为true,当使用Thread.interrupted()得到标志位时,做出判断后还会将其改为false,thread.isInterrupted();//设置标志为为true。

public static void main(String[] args) {
        TestThread thread = new TestThread();
        thread.start();
        thread.isInterrupted();//设置标志为为true
  }
  public void run() {
  // while (!Thread.interrupted()){         通过这种方式testThread最后打印的结果还是false
        while (!isInterrupted()){           //通过这种方式testThread最后打印的结果还是true
            System.out.println("正在运行的线程:"+Thread.currentThread().getName()+"=="+isInterrupted());
        }
        System.out.println("testThread:"+isInterrupted());
    }

另外当发生异常或者中断时
如下代码

 public void run() {
                while (!isInterrupted()){
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();//进入异常
                        System.out.println("现在:"+isInterrupted());
                        //可以在这里再手动修改一下    Thread.interrupt();
                    }
                    System.out.println("正在运行的线程:"+Thread.currentThread().getName()+"=="+isInterrupted());
                }            System.out.println("testThread:"+Thread.currentThread().getName()+"=="+isInterrupted());
            }
     public static void main(String[] args) throws InterruptedException {
          TestThread thread = new TestThread();
        thread.start();
        thread.sleep(450);//设置这个数引发中断异常
        thread.interrupt();
    }

由此可知,当发生中断异常时会把标志位由true改成false,除非再手动调用interuppt()方法(记得在此之前进行资源回收)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值