Java多线程对我来说一直是个高级而且神秘的东西,那么今天有幸也正好有时间讲解一下Java的多线程。
首先线程的概念及定义就不说了
1.创建线程三种方式:1.继承Thread 2.实现Runnable接口 3.实现Callable接口
继承Thread
public class FirstThread extends Thread{ private int i; public void run(){ for(;i<100;i++){ System.out.println(getName()+" "+i); } } public static void main(String[] args){ for(int i=0; i<100; i++){ System.out.println(Thread.currentThread().getName()); if(i == 20){ new FirstThread().start(); new FirstThread().start(); } } } }
实现Runnable
public class SecondThread implements Runnable{ private int i; public void run(){ for(;i<100;i++){ System.out.println(Thread.currentThread().getName()+" "+i); } } public static void main(String[] args){ for(int i=0; i<100; i++){ System.out.println(Thread.currentThread().getName()+" "+i); if(i == 20){ SecondThread st = new SecondThread(); new Thread(st,"新线程1").start(); new Thread(st, "新线程2").start(); } } } }
实现Callable
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class ThirdThread implements Callable<Integer>{ public Integer call(){ int i=0; for(;i<100;i++){ System.out.println(Thread.currentThread().getName()+"的循环变量i的值"+i); } return i; } public static void main(String[] args){ ThirdThread rt = new ThirdThread(); FutureTask<Integer> task = new FutureTask<Integer>(rt); for(int i=0; i<100; i++){ System.out.println(Thread.currentThread().getName()+"的循环变量i的值"+" "+i); if(i==20){ new Thread(task, "有返回值的线程").start(); } } try{ System.out.println("子线程的返回值:"+task.get()); }catch(Exception e){ e.printStackTrace(); } } }
简单说明:Callable更像是Runnable的增强版,所以可以归为一类,所以大体上创建线程的方式只有两种,对于使用Thread方式好,还是Runnale方式好,这里推荐Runnale方法,因为如果采用继承Thread方法就不能继承其它父类,相比之下,Runnale则更加灵活
2.线程的生命周期(这里以网上收集的一幅图来说明)
3.线程的控制
(1)join()方法:作用是如果在运行当前线程的时候中途你又调用另一线程,那么当前线程先暂停执行,把另一线程执行完后在继续执行
(2)守护线程:作用是为其它线程提供服务,而且又在后台运行,所以又叫后台线程
(3)线程睡眠:使用sleep方法,可以理解为在睡眠这段时间内指定的线程都不会执行(就让它一直睡下去),即在指定时间内的线程无效(这是为了帮助理解,片面的说法)
(4)线程让步:使用yield方法,可以理解为在指定时间内指定的线程不执行,过了这段时间,在执行(只是暂停,目的是为了想让其它线程执行)
(5)优先级的设置:字面意思就可以理解,就不再阐述
4.线程的同步(意思就是线程按先后顺序依次执行)
试想如果线程不同步,就会出现不按顺序执行,就会出现线程同时执行,这样就会出大问题
如果多个线程异步(不同步)访问,就出问题了,比如典型的银行存款取款问题,这是类似生产者和消费者的问题(其实很简单,意思就是一个人赚钱一个人花钱),如果存款和取款同时操作,我们就会出现很多问题,比如作为取款方,系统要对取款进行钱的减量操作,但是另一个人又在存,而且同一时间,我的减量操作到底是存了后的还是存了前的?作为存款方,系统对存款进行增量操作,但是我的增量操作,到底是取了后的还是取了钱的,那么系统就会出问题了,那么银行要是这么搞,你会高兴还是悲伤呢,自己的钱老是出问题。
所以,多个线程访问同样资源时,我们要进行同步操作,同步操作有以下几种方式
(1).使用synchronized:可以对方法进行锁定,可以理解为,每个线程有一把钥匙,你执行这个方法,方法使用synchronized就会加锁,你要你的线程只有一个能到达锁前开锁,每个线程访问都会对该方法锁定;也可以对代码块锁定,大家都知道,函数方法就是代码写的,那么方法能锁定代码块也能锁定
对方法锁定
public synchronized void 方法名(锁定的参数(比如银行账户)){
方法的实现
}
对代码块的锁定
synchronized(锁定的参数(比如银行账户)){
代码的实现
}
(2).使用同步锁Lock:这个方法比synchronized更强大,主要体现在:更为广泛的锁定操作,因此实现起来更为灵活,比较好的东西也比较重要,但是使用方法差不多,所以这里不做详细介绍
4.2.死锁:是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去(感觉不是很好理解,可以查阅相关资料)
5线程的通信,有以下几种方式
(1)对于synchronized借助Object类提供的wait(),notify,nitifyAll()三个方法,我们从字面意思就可以大体理解他们的作用
(2)如果使用Lock对象,则要使用Condition来控制,借助await(),signal,signalAll(),是不是感觉和synchronized的通信控制比较相似,那么我们也可以以字面意思来理解它们的大体作业
(3)使用阻塞队列(BlockingQueue),这是Java5提供的一个接口BlockigQueue,使用put(),take(),方法,
6.线程组,线程池,线程相关的工具ThreadLoacl及安全的线程集合(主要对不安全的集合进行包装,使之安全)以后再做介绍
最后是线程的使用:
我们学习了线程最为困惑的是什么时候我们使用多线程:
1.要处理的数据比较庞大(比如对数据库的数据进行分析)
2.并发控制,一般采用线程池技术
时间不够了,就暂时写到这里吧,写的不是很详细,有时间再补充上去,对了,这里推荐一个网站给大家吧,并发编程网,我感觉里面的东西非常好,所以有时间一定要去看看,如果你对多线程技术感兴趣的话