Java中多线程总结

 

1.     创建线程有两种方式:

1)扩展java.lang.Thread

    public class ThreadTest extends Thread{ }

2)实现Runnable接口

    public class ThreadTest implements Runnable{ }

2.     Thread类代表线程类,有两个最主要的方法:

1run() 包含线程运行时所执行的代码。

   用户的线程类只需要继承Thread类,覆盖Thread类的run()方法即可。在Thread类中的run()方法没有招聘任何异常,所以Thread子类的run()方法也不能声明招聘任何异常。

2start() 用于启动线程。

3.线程的运行过程:

   1)主线程与用户自定义的线程并发运行

   2)多个线程共享一个对象的实例变量

   3)不要随便覆盖Thread类的start()方法,假如一定要覆盖,就在重新定义的start()方法首语句中调用super.start()方法。

   4)一个线程只能被启动一次

4.线程的状态转换:

   1)新建状态

      new语句创建的线程对象处于新建状态,此时它和其他Java对象一样,仅仅在堆区中被分配了内存。

   2)就绪状态

      当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态,JVM会为它创建方法调用栈和程序计数器。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。

   3)运行状态

      处于这个状态的线程占用CPU,执行程序代码。在并发运行环境中,如果计算机只有一个CPU,那么任何时刻只会有一个线程处于这个状态。如果计算机有多个CPU,那么同一时刻可以让几个线程占用不同的CPU,使它们都处于运行状态。只有牌就绪状态的线程才有机会转到运行状态。

   4)阻塞状态

      阻塞状态是指线程因为某些原因放弃CPU,暂时停止运行。当线程处于阻塞状态时,JVM不会给线程分配CPU,直到线程重新进入就绪状态,它才有机会转到运行状态。

      阻塞状态可分为以下3

      1))位于对象等等池中的阻塞状态:当线程牌运行状态时,如果执行了某个对象的wait()方法,JVM就会把线程放到这个对象的等待池中。

      2))位于对象锁池中的阻塞状态:当线程牌运行状态,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,JVM就会把这个线程放到这个对旬的锁池中。

      3))其他阻塞状态:当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O请求时,就会进入这个状态。

         当一个线程执行System.out.println()或者System.in.read()方法时,就会发出一个I/O请求,该线程放弃CPU,进入阻塞状态,直到I/O处理完毕,该线程才会恢复运行。

   5)死亡状态

      当线程退出run()方法时,就进入死亡状态,该线程结束生命周期。线程有可能是正常执行完run()方法而退出,也有可能是遇到异常而退出。不管线程正常结束还是异常结束,都不会对其他其他线程造成影响。

      Thread类的isAlive()方法判断一个线程是否活着,当线程处于死亡状态或者新建状态时,该方法返回false,在其余状态下,该方法返回true.

5.线程的调度:

  有两种调度方式:分时调度模型和抢占式调度模型。

  分时调度模型是指让所有线程轮流获得CPU的使用权,并且平均分配每个线程占用CPU的时间片。

  Java虚拟机采用抢占式高度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中线程的优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃CPU

  线程的调度不是跨平台的,它不仅取决于JVM,还依赖于操作系统。

6Thread类的setPriority(int)getPriority()方法分别用来设置优先级和读取优先级。

   1MAX_PRIORITY:取值为10,表示最高优先级

   2MIN_PRIORITY:取值为1,表示最低优先级

   3NORM_PRIORITY:取值为5,表示默认的优先级

  如果希望程序能移植到各个操作系统中,应该确保在设置线程的优先级时,只使用MAX_PRIORITYMIN_PRIORITYNORM_PRIORITY这三个优先级。这样才能保证在不同的操作系统中,对同样优先级的线程采用同样的调度方式。

7sleep()方法与yield()方法的区别:

   1sleep()方法会给其他线程运行的机会,而不考虑其他线程的优先级,因此会给较低优先级线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。

   2)当线程执行了sleep()方法后,将转到阻塞状态;当线程执行了yield()方法后,将转到就绪状态。

   3sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明招聘任何异常。

   4sleep()方法比yield()方法具有更好的可移植性。

8.等待其他线程结束:join()方法

   当前运行的线程可以调用另一个线程的join()方法,当前运行的线程将转到阻塞状态,直至另一个线程运行结束,它才会恢复运行(确切的意思是指线程从阻塞状态转到就绪状态)。

9.定时器Timer

   Timer timer=new Timer(true);

   TimerTask task=new TimerTask(){ }

   timer.schedule(task,10,500);//定时器将在10毫秒后开始执行task任务,以后每隔//500毫秒重复执行一次task任务。

   timer.schedule(task,10);//只执行一次task任务。

10.同步代码块

    为了保证每个线程能正常执行原子操作,Java引入了同步机制,具体做法是在代表原子操作的程序代码前加上synchronized标记,这样的代码被称为同步代码块。

    有两种方式加代码锁:

1)  直接在方法前加synchronized标记,

public synchronized void add(){... }

2) 在方法内形成synchronized标记的代码块

   public void add(){synchronized(this){... }}

取得对象锁:

1)  假如这个锁已经被其他线程占用,JVM就会把这个消费者线程放到Stack对象的锁池中,消费者线程进入阻塞状态。在Stack对象的锁池中可能会有许多等待锁的线程。等到其他线程释放了锁,JVM会从锁池中随机取出一个线程,使这个线程拥有锁,并且转到就绪状态。

2)  假如这个锁没有被其他线程占用,消费者线程就会获得这把锁,开始执行同步代码块。在一般情况下,消费者线程只有执行完同步代码块,才会释放锁,使得其他线程能够获得锁。

释放对象锁:

1)  执行完同步代码块,就会释放锁。

2)  在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。

3)  在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进入对象的等待池。

4)  在执行同步代码块的过程中,执行了锁所属对象的notify()方法后,JVM会从对象的等待池中随机选择一个线程,把它转到对象的锁池中。

线程同步的特征:

1)  如果一个同步代码块和非同步代码块同时操纵共享资源,仍然会造成对共享资源的竞争。因为当一个线程执行一个对象的同步代码块时,其他线程仍然可以执行对象的非同步代码块。

2)  每一个对象都有惟一的同步锁。

3)  在静态方法前面也可以使用synchronized修饰符。

4)  当一个线程开始执行同步代码块时,并不意味着必须以不中断的方式运行。进入同步代码块中的线程也可以执行Thread.sleep()或者执行Thread.yield()方法,此时它并没有释放锁,只是把运行机会(即CPU)让给了其他的线程。

5)  synchronized声明不会被继承.

11.死锁:

    当一个线程等等由另一个线程持有的锁,而后者正在等待已被第一个线程持有的锁时,就会发生死锁。JVM不监测也不试图避免这种情况,因此保证不发生死锁就成了程序员的责任。

12

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值