java线程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java线程相关的知识,希望对你有一定的参考价值。

java线程

1.区分线程、进程的区别
进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。 (进程中的线程数量是有限的)
线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。
多线程的并发运行:指可以有两个或两个以上的线程同时运行( 同时执行是人的感觉,在线程之间实际上轮换执行。
 
2.同步与异步(不是在时间上的同步与异步)
同步:更多是指一个事件依赖于另一个事件的发生,效率比较低( 为了防止多个线程访问一个数据对象时,对数据造成的破坏 )
锁机制:在线程中当对其中一个线程进行数据改变时,会对该线程进行枷锁,等到该空间释放后,其他在执行,有点偏向于是单线程
               在java中,每个对象都有一个内置锁,当代码运行到synchronized关键字时,会获得与正在执行代码相关对象(this)的锁
               一个对象只有一个锁,当一个进程获得该对象的锁时,其余进程便不能再获取该对象,进入等待状态,直至该线程释放该对象
               而当该对象进入休眠状态时,也不会释放该对象的锁
               当两个进程互相等待对象释放被锁对象时,就会进入死锁状态
               而一个线程可以获得多个锁,同时只能同步方法,不能同步变量和类
               而当同步静态方法时,则是获得整个类的锁
         静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。
两种方法:
public int fix(int y) {
        synchronized (this) {
            x = x - y;
        }
        return x;
    }
 
 
public synchronized int getX() {
        return x++;
    }
 
在使用synchronized关键字时候,应该尽可能避免在synchronized方法或synchronized块中使用sleep或者yield方法,因为synchronized程序块占有着对象锁,你休息那么其他的线程只能一边等着你醒来执行完了才能执行。不但严重影响效率,也不合逻辑。
 
异步:则是指几个线程互不相干,两者可以分开进行
 
3.线程状态:创建+就绪+运行+结束+阻塞(实现Thread 类或 实现runabled接口、callable接口)
创建:new Thread()
          在调用start()方法之后:发生了一系列复杂的事情
      启动新的执行线程(具有新的调用栈);
      该线程从新状态转移到可运行状态;
      当该线程获得机会执行时,其目标run()方法将运行。
就绪:start()方法运行,进入可调度状态
等待/阻塞/睡眠:还没有满足条件时,需要的等待条件满足,解决后,又回到就绪状态等待CPU调度
            [通过改变线程的优先级别可改变线程的运行顺序,但线程的优先级别不可依靠]
死亡:线程的run()方法运行结束,进入死亡状态,不能再次使用start()方法,会报错( java.lang.IllegalThreadStateException异常 )
 
线程的调度是JVM的一部分,在一个CPU的机器上上,实际上一次只能运行一个线程。一次只有一个线程栈执行。(多个线程轮换执行:同时的假象)
 
同一个线程不能开始两次
 
线程离开原先状态的三种方法:

1、休眠:调用Thread.sleep():使当前线程睡眠至少多少毫秒(尽管它可能在指定的时间之前被中断)。

2、让步:调用Thread.yield():不能保障太多事情,尽管通常它会让当前运行线程回到可运行性状态,使得有相同优先级的线程有机会执行。
3、合并:调用join()方法:保证当前线程停止执行,直到该线程所加入的线程完成为止。然而,如果它加入的线程没有存活,则当前线程不需要停止。
     比如a加入b线程后,b线程需等a线程执行完后才可以执行
(两线程互相join时,出现死锁现象)
设置优先级别:t1.setPriority(1-10);
 
4.守护线程:顾名思义,守护线程就是专门为用户线程服务的线程,他与用户线程最明显的区别就在于:当jvm 中存在用户线程时,jvm就不会退出,而当只剩下守护线程时,jvm就会退出服务,并且守护线程也会想对应终止服务。因为他最大的任务就是服务用户线程,当用户线程不存在时,它便没有存在的必要了。jvm中典型的守护线程就是垃圾回收线程。
它主要通过设置线程对象的daemon属性,要在调用之前使用;所以在设置daemon的线程会出现执行一半就停止的现象,在守护线程中创建的线程也是属于守护线程,需慎用守护线程
 
 
 
Thread类中run()和start()方法的区别如下:
run()方法:在本线程内调用该Runnable对象的run()方法,可以重复多次调用;
start()方法:启动一个线程,调用该Runnable对象的run()方法,不能多次启动一个线程;
 
5.线程交互:指线程之间的信息通知和相应处理
void notify() 
          唤醒在此对象监视器上等待的单个线程。
 void notifyAll() 
          唤醒在此对象监视器上等待的所有线程。
 void wait() 
          导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法。
必须从同步环境内调用wait()、notify()、notifyAll()方法。线程不能调用对象上等待或通知的方法,除非它拥有那个对象的锁。
wait()、notify()、notifyAll()都是Object的实例方法。与每个对象具有锁一样,每个对象可以有一个线程列表,他们等待来自该信号(通知)。线程通过执行对象上的wait()方法获得这个等待列表。从那时候起,它不再执行任何其他指令,直到调用对象的notify()方法为止。如果多个线程在同一个对象上等待,则将只选择一个线程(不保证以何种顺序)继续执行。如果没有线程等待,则不采取任何特殊操作。
 
简单理解就是,每个对象上都有一个锁,当执行wait()时,其他线程会放弃该对象的锁,而执行wait()的线程将获得该对象的锁
当执行notify()时,线程却未必会放弃锁,会当他执行完之后再放弃锁
 
6.线程异常:线程的错误只能在本地处理,不能往上抛,就算往上抛,也只会现实问题出现在线程内
 
7.线程池
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取有限个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。
在Java5中,需要了解的是java.util.concurrent.Executors类的API,这个类提供大量创建连接池的静态方法
虽然线程池的顶级接口是executors,但这个只是一个执行线程的工具,executorsService才是线程池的接口
 
  • newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
  • newFixedThreadPool创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
  • newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
  • newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求,可在给定延期后或定期执行
     pool.schedule(t4, 10, TimeUnit.MILLISECONDS);(延时执行)
 
publicclass Test {
        publicstaticvoid main(String[] args) {
                //创建一个可重用固定线程数的线程池
                ExecutorService pool = Executors.newFixedThreadPool(2); //固定大小的线程池,每次执行两条线程
                //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口
                Thread t1 = new MyThread();
                Thread t2 = new MyThread();
                Thread t3 = new MyThread();
                Thread t4 = new MyThread();
                Thread t5 = new MyThread();
                //将线程放入池中进行执行,按任务的提交顺序执行,线程的命名顺序也是按线程池中执行顺序来命名
                pool.execute(t1); 
                pool.execute(t2); 
                pool.execute(t3); 
                pool.execute(t4); 
                pool.execute(t5); 
                //关闭线程池
                pool.shutdown(); 
        } 
 
可以自定义线程池:(主要是新建ThreadPoolExecutor对象)
  ThreadPoolExecutor pool = newThreadPoolExecutor(2,3,2,TimeUnit.MILLISECONDS,bqueue);
 
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime,TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

参数:

corePoolSize -池中所保存的线程数,包括空闲线程。

maximumPoolSize -池中允许的最大线程数。

keepAliveTime -当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。

unit - keepAliveTime参数的时间单位。

workQueue -执行前用于保持任务的队列。此队列仅保持由execute方法提交的Runnable任务。
 
8.并发协作:生产消费模型

实际上,准确说应该是“生产者-消费者-仓储”模型,离开了仓储,生产者消费者模型就显得没有说服力了。

对于此模型,应该明确一下几点:

1、生产者仅仅在仓储未满时候生产,仓满则停止生产。(生产者调用wait())

2、消费者仅仅在仓储有产品时候才能消费,仓空则等待。(消费者调用wait())

3、当消费者发现仓储没产品可消费时候会通知生产者生产。(消费者调用notify(),该对象上的等待线程将继续执行)
4、生产者在生产出可消费产品时候,应该通知等待的消费者去消费。(生产者调用notify(),该对象上的等待线程将继续执行)
 
注意:需要在同步方法中调用wait和notify方法
 
9.有返回值的线程
java5后出现有返回值的线程: 获取一个Future的对象,在该对象上调用get就可以获取到任务返回的Object了。
ExecutorsService exec=new  ExecutorsService();
Future<T> future=exec.submit(runnable/callable);
System.out.println(fucture.get().toString());
 
 
这是我学习java线程时整理的一下笔记,主要来自博主you Richer的博客
网址:http://www.cnblogs.com/riskyer/p/3263032.html
 
 
 





















以上是关于java线程的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程 2.线程安全

Java多线程 4.线程池

Java多线程 1.认识Java线程

java 子线程 回调 主线程

Java线程池

java 如何获得线程池中正在执行的线程数?