java多线程基础

来源:互联网 发布:单片机怎么处理at指令 编辑:程序博客网 时间:2024/05/21 19:26

一、线程概念理解及相关术语
多任务-程序-进程-线程
操作系统的多任务模式:同一时刻运行多个程序。
进程:执行中的程序,是程序运行的基本单位,各进程之间具有独立内存空间和系统资源,故其数据和状态相对其他进程是独立的。
线程:是进程中单个顺序的流控制,多个线程时,可一次运行多个程序块(多个执行路径),线程之间共享内存空间和系统资源(与进程的本质区别),故线程间可通信和同步(能相互影响),且比进程之间通信要更容易,节省系统资源。

Thread类:java中用Thread类来描述线程。其中的run方法存储需要另开线程运行的代码。

二、线程的创建和启动
1.继承Thread类并重写run方法:可新建一个类继承Thread类并重写run方法(也可以匿名内部类形式),然后创建对象,直接调用start方法。
MyThread mt = new MyThread( );
mt.start( );
注意:如果创建多个该类对象来开启多个线程,因对象间数据相对独立,无法像Runnable一样直接同步。
缺点:受java单继承限制,故实际开发使用较少。

2.实现Runnable接口并重写run方法:新建一个类实现Runnable接口并重写run方法,然后新建该类对象,再新建Thread对象(可多个)接受该类对象,然后调用start方法。
MyRun mr = new MyRun ( );
 Thread t = new Thread(mr); 
t.atart( );

3.java程序运行时,至少有两个线程,一个是主函数运行而开启的,线程名为main,另一个是垃圾回收机制(GC)的线程。

4.多个线程运行时,不是按排队一个个轮流来获得cpu的时间片,而是互相“抢夺”,随机获得,若定义了线程的优先级,则可使高优先级的线程更大概率获得运行资格(抢夺得手)。

5.start与run方法:run方法定义时用于存储代码,调用时同普通方法调用,不开启新线程,start方法用于开启新线程。

三、线程状态及相关方法




1、sleep方法:设定时间(毫秒值,为long类型),时间到自动回复到可运行状态(即图中的运行或阻塞状态)。
2、wait方法:让线程冻结,并等待被notify或notifyAll唤醒,无法如sleep一般自动恢复
3、notify方法和notifyAll方法:唤醒进程
4、wait、notify、notifyAll方法从Object继承而来,可由任意对象调用
5、使用Lock锁时,wait、notify、notifyAll可用Condition接口中的方法await,signal,signalAll。

四、线程间通信相关
1、通信原理:操作内存中的共享数据,涉及到数据同步,避免死锁等问题。

五 线程同步
1、为什么要同步?
若一个线程只操作完一部分数据,就被另一线程抢夺执行权而又对数据操作,可能导致数据错乱(线程不安全)。
故需要限制:只能一个线程对数据操作完后,才允许另一线程对数据进行操作。

2、同步的前提:
必须要有两个或者两个以上的线程。(当然几个线程操作的也应该有相同的数据,否则无所谓同步)
必须是多个线程使用同一个锁。

3、同步方法:
同步代码块和同步函数。
步骤:
1、建立一个类来定义要输入输出的对象
2、建立输入和输出两个类,因多线程,故需实现Runnable接口
3、输入和输出类中接收对象而建立自己的输入输出对象
4、当输入正在执行时,不允许输出,以避免混乱,故需加锁
5、当输入已完成一次时,不再输入,线程等待,归还锁:避免一次输入多个相同值,后一次输出多个相同值


JDK1.5版本前,使用synchronized关键字,当使用同步代码块时,锁为任意对象。使用同步函数时,锁为this,即当前对象(静态函数时,锁为.class文件对象)。
JDK1.5版本后,直接使用Lock接口对象,并将Object中的wait,notify notifyAll,替换成了Condition接口对象,相应方法为await,signal,signalAll方法。其将锁显化,可更好地避免死锁的情况。
     Lock lock = new ReentrantLock();
     Condition condition_pro = lock.newCondition();
     Condition condition_con = lock.newCondition();
4.死锁
最常见形式为:线程1拿到了对象A的锁,且正在等待对象B的锁,而线程2拿到了对象B 的锁,正在等待A的锁。
多出现在同步嵌套的情况下,需避免。


六、Thread类的操作
1、设置与获取属性类
Thread.currentThread():获取当前线程对象
setName():设置线程对象名称
getName():获取线程对象名称
setPriority():设置线程对象优先级
getPriority():获取线程对象优先级
isInterruped()判断线程是否中断,返回boolean
toString() :返回线程名称 优先级 线程组
2、操作
interruput:将线程中断,会抛出InterruptedException
join:加入线程,类似于插队:当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。该方法需在线程开启后才调用。该方法还有带参数的两个格式,可设定jion的时间,时间到后又分离,分别执行。

yield:暂停当前线程,释放执行权,可定义在循环中,减缓循环执行速度,达到交替执行效果
setDeamon(boolean):在线程开启(start)之前前设置为true时,该线程变成守护线程(后台线程),当只剩下守护线程时,jvm退出

3、异常:
InterruptedException
可由sleep、wait方法,Condition的await方法抛出,一般是当线程在该状态中被打断时出现


七、多线程编程思想

典型问题:生产者消费者问题
问题要求:生产者和消费者交替生产产品,生产一个,消费一个,同时对生产的产品计数。

思路:
1、对象提取:生产者、消费者、产品
2、对象分析:产品对象:具有属性——数量,并对外提供方法:数量增加(生产),数量显示(消费);
生产者对象:传入产品对象来构造函数,并调用产品中的生产方法,
消费者对象:传入产品对象(需与生产者同一个)来构造函数,并调用产品中的消费方法,显示消费的是第几个产品。
3、多线程设计:多个生产者来生产,多个消费者来消费——多线程由生产者消费者对象来开启——复写run方法并循环,而同步则是放在资源的操作方法内
4、同步的细节问题:标记,等待与唤醒

public class ProConDemo1{public static void main(String[] args){Resource res = new Resource();new Thread(new Producer(res)).start();//两个生产者和两个消费者new Thread(new Consumer(res)).start();new Thread(new Producer(res)).start();new Thread(new Consumer(res)).start();}public static void sop(Object obj){System.out.println(obj);}}class Resource{private int num ;private boolean flag;public synchronized void produce(){while(flag)//判断标记,如果已经有生产者生产了一份产品,那么标记是true,生产者都wait,释放执行权{try{this.wait();} catch (InterruptedException e){e.printStackTrace();}}num++;//标记为假,执行到这里,就生产一份产品,并打印,ProConDemo1.sop(Thread.currentThread().getName()+"-----生产者生产商品-"+num);flag = true;//将标记改为真——使所有生产者停产wait,让消费者去消费掉此份产品this.notifyAll();//并且唤醒所有wait的进程,以免全体等待。}public synchronized void consume(){while(!flag)//判断标记,如果已经有生产者生产了一份产品,那么标记是true,消费者不再等待,往下执行消费过程{try{this.wait();} catch (InterruptedException e){e.printStackTrace();}}ProConDemo1.sop(Thread.currentThread().getName()+"---消费者消费商品-"+num);flag = false;//一次消费完后,让所有消费者停止消费,让生产者开始生产this.notifyAll();//并且唤醒所有wait的进程,以免全体等待。}}class Producer implements Runnable{private Resource r;Producer(Resource r){this.r = r;}public void run(){while(true)//一直生产{r.produce();}}}class Consumer implements Runnable{private Resource r;Consumer(Resource r){this.r = r;}public void run(){while(true)//一直消费{r.consume();}}}



0 0