多线程技术的理解
一.多线程的重点类容:
1.创建多线程的方式(两种)。
2.线程运行的状态(五种)。
3.线程的安全问题。同步代码块,同步函数,lock接口。
4.死锁。
5.线程通信。
6.用executors产生单线程,创建线程池。
进程和线程是什么:一个运动的程序,在内存上分配的空间就是进程(如电脑上任务管理器中的进程)线程就是程序的一个执行控制单元。一个进程中往往偶多个线程,至少有一个线程。
怎么去解多线程?进程好比一个商场,线程好比一个人,多线程可以比做很多人去逛商场。线程可比排队逛商场,里面的人出来了才能在进去一个人。对商场而言,多线程的效率肯定要高很多,程序的多线程也是一样。
1创建线程的方式
(a)定义一个类继承Thread ,由子类复写run方法。
步骤:
1,定义类继承Thread类;
2,目的是复写run方法,将要让线程运行的代码都存储到run方法中;
3,通过创建Thread类的子类对象,创建线程对象;
4,调用线程的start方法,开启线程,并执行run方法。
代码:class Demo extends Thread{
public void run()
{
//多线程代码
}
}
//说明:如果Demo有参数,run前面要有有参的构造函数。
/* Demo d = new Demo();
这个时候创建子类对象同时也创建了多线程对象*/
(b) 创建线程的第二种方式:
定义一个类实现一个接口Runnable。
步骤:
1,定义类实现Runnable接口。
2,覆盖接口中的run方法(用于封装线程要运行的代码)。
3.创建实现Runable接口的子类对象
4.用Thread创建线程对象,并接受接口的子类对象
5.用Thread的start方法开启线程,运行接口的run方法。代码:class Demo implements Runnable{
public void run(){
//多线程代码
}
}
public class duoXianCheng {
public static void main(String[] args){
Demo d = new Demo();
Thread t1 new thread(d);
Thread t2 new thread(d);
t1.start();
t2.start();
}
}<span style="font-family: Arial; ">说明:接口的子类对象要作为参数传递给线程对象</span>
为什么会有Runnable接口:Thread类只能被单继承,Runnable可以被多实现,弥补了Thread的缺陷。
2.线程有5中状态
被创建:start()
运行:具备执行资格,同时具备执行权;
冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;
临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;
消亡:stop()
(注意:执行权和执行资格的变化)
3.线程安全:
为什么会产生线程安全问题?
以商场卫生间为例,整个商场的人都去上厕所,大家挤着谁也上不了,这个时候商场定义一个规则,大家排队上厕所,问题就解决了。 厕所是共享资源。排队可以解决问题。
线程安全问题发生的条件:多条语句在操作同一个共享资源,就能发生多线程安全问题,排队可以解决这个问题
(a) synchronized(对象){
(被同步的代码)(共享数据)
}
(b) public synchronizedvoidmethod (){
(被同步的代码)(共享数据)
}
(c) Lock lock= newReentrantLock();
public void method() {
lock.lock();
(被同步的代码)共享数据
lock.unlock();
}
}
注意:(a)中对象就是锁,任意对象都可以作为锁。
(a) 中同步函数中有没static锁不一样,
没有static锁是this
有static 锁是类名.class
(懒汉式单例模式考虑线程安全时候会用到)
(b) 中同步代码一般要try ,lock.unlock要放在finally中代码运行完后一定要释放锁。
单核cpu是在多线程快速切换运行的,也就是在某一个时刻,只有一个线程在运行。
4死锁:
同步嵌套同步是最常见的死锁。
例如:最常见的原因是锁的嵌套循环,例如if else判断语句里面,当线程满足if条件,获得A锁执行if里面的同步,A同步里面还嵌套
了一个同步,需要B锁,这时线程切换到else条件,获得了B锁执行里面的同步,这个同步里面也嵌套了一个同步,需要获得A锁才能执行。if和else里面的同步都不放锁,就形成了死锁。
解决办法:用同一个锁。
5线程通信
思路: 多个线程在操作同一个资源,但是操作的动作不同。
1.将资源封装成对象。
2.将线程执行的任务封装成对象。(run方法)
3.
完成线程通信需要用到等待唤醒机制
wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。
notify:唤醒线程池中某一个等待线程。
notifyAll:唤醒的是线程池中的所有线程。
注意: wait和sleep区别: 分析这两个方法:从执行权和锁上来分析:
wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。
sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
wait:线程会释放执行权,而且线程会释放锁。
Sleep:线程会释放执行权,但不是不释放锁。
</pre><pre name="code" class="java">经典代码:生产者和消费者:
//多线程间通信问题 <生产烤鸭问题>
/*resoures producer consumer 和主函数创建线程4个类。resoures定义生产和消费的方法,producer和consumer定义接受resoures的构造函数
* ,并实现多线程。*/
public class ProuderComsumerDemo {
public static void main(String[] args) {
resoures r = new resoures();
producer pro = new producer(r);
consumer con =new consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//资源
class resoures {
//定义变量:生产什么。生产多少了,生产与消费之间的切换变量
private String name;
private int count=0;
private boolean falg =false;
//生产方法
public synchronized void set(String name){
// 什么时候生产
while(falg)
try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}//睡觉
//生产
count++;
if(count==1000){
}
this.name = name+count;
System.out.println(Thread.currentThread().getName()+"生 产 了"+this.name);
//生产完了要告诉消费者
falg=true;
notifyAll();
}
//消费方法
public synchronized void out(){
while(!falg)
try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"消 费 了"+this.name);
falg=false;
notifyAll();
}
}
//生产者
class producer implements Runnable{
private resoures r;
public producer(resoures r) {
super();
this.r = r;
}
@Override
public void run() {
while(true){
r.set("烤鸭");
}
}
}
//消费者
class consumer implements Runnable{
private resoures r;
public consumer(resoures r) {
super();
this.r = r;
}
@Override
public void run() {
while(true){
r.out();
}
}
}
6.(a)用executors产生单线程,创建线程池。
ExecutorService pool = Executors.newSingleThreadExecutor();
pool.execute(new Runnable(){})
(b)/产生一个单线程,创建定时器
ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);
timer.scheduleAtFixedRate(
new Runnable(){})
(注意:executors必须记住。)