------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
Day11——多线程 java.lang
*01-线程(概述)
1、进程:是一个正在执行的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
2、线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。一个进程中至少有一个线程
3、JavaVM 启动时会有一个进程java.exe。该进程至少一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中,该线程称之为主线程
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收的线程
*02-创建线程-继承Thread类
通过对API的查找,java提供了对线程这类事物的描述,就是Thread类。在java.lang包中
1 、创建线程的第一种方式,继承Thread类
步骤:a\定义类继承Thread;
b\复写Thread类中的run方法;
目的:定义线程需要执行的代码,run方法就是存储线程要执行的代码
c\调用线程的strat方法(该方法两个作用:启动线程,调用run方法)
03-多线程(创建线程-run和start特点)
为什么要复写run方法?
定义线程需要执行的代码,run方法就是存储线程要执行的代码
为什么不直接用d.run();?而要用d.start();?
d.run()仅仅是对想调用方法,而线程创建了,并没有运行。
05-多线程(线程运行状态)
注意:还有一个状态(临时状态) 阻塞 具备执行资格,但没有执行权,可能cpu在执行别的线程。当冻结的线程被唤醒了他最初会到阻塞状态,再判断cpu是否空闲,空闲时到运行状态
阻塞
sleep() ,wait()
被创建 start() ------------> 运行 < ------------------------->冻结(放弃执行资格)
| Sleep()完 notify()
Run()结束
或 Stop()方法
Stop() 消亡
06-多线程(获取线程对象以及名称)
发现运行结果每一次都不同,因为多个线程都在获取cpu的执行权,在某个时刻,只能有一个程序在运行(除多核外),cpu在做快速的切换,以达到看上去是同时执行的效果。我们可以形象的把多线程的运行行为看作是相互抢夺cpu的执行权、
1 、线程都有自己的名称 Thread-编号 编号从0 开始
2 、也可以自定义线程名称,
在线程构造方法中ZiThread(String name){super(name)}
3 、设置线程名称:setName()或者构造函数
获取线程名称:getName()
4 、staticThread currentThread();获取当前线程对象
08-创建线程-实现Runnable接口
1、Runnable接口应该由那些打算通过某一线程执行其实例的类来实现,
类必须定义一个run的无参数方法
2 、创建线程的第二种方式,实现Runnable接口
步骤:a\定义类实现Runnable接口;
b\覆盖Runnable接口中的run方法;
将线程要运行的代码存放在改run方法中
c\通过Thread类建立线程对象;
d\将Runnable接口的子类对象传递给Thread的构造函数
3 、为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为自定义的run方法所属的对象是Runnable接口的子类对象;所以要让线程去指定 指定对象的run方法,就必须明确该run方法所属的对象
**********注意:实现接口和继承方式有什么区别??????
实现方式的好处:避免了单继承的局限性;在定义线程时,建议使用实现方式
两种方式的区别:
继承Thread:线程代码存放在Thread子类run方法中
实现Runnable:线程代码存放在接口的子类run方法中
09-多线程(多线程的安全问题) (售票问题同步后代吗)
class Ticketimplements Runnable
{
private int tick = 100; //堆内存中
Object obj = new Object();
// public (synchronized) void run()(也可以把synchronized放函数上做为修饰符)
public void run()
{
while(true)
{
synchronized (obj)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"^^sale:"+tick--);
}
}
}
}
}
class TicketDemo
{
public static void main(String[]args)
{
Ticket t = new Ticket ();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
1\买票问题——有四个线程对全局变量Tick操作,当Tick>0时售票(线程操作Tick)发现打印出了0,-1,-2等错票
2\多线程运行出现安全问题原因:
当多个线程在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,(然后让他睡眠),还没执行完,另一个线程参与进来执行。导致共享数据的错误
3、解决办法
对多个操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行
10-多线程(多线程同步代码块)
java对多线程的安全问题提供了专业的解决方式(同步代码块)
synchronized(对象){需要被同步的代码(也就是有共享数据的代码)}
4、对象如同锁,持有锁的线程可以在同步代码块中执行,没有持有锁的线程即使获取cpu执行权也进不去,因为没有获取锁(信号量:0,1)当信号量为1时代表可以通过
火车上的卫生间——经典
5、同步的前提:必须要有两个或以上线程;必须是多个线程使用同一锁*(同一个对象)
好处:解决了多线程的安全问题
弊端:多个线程需要判锁,较为耗费资源
11-多线程(多线程-同步函数)
public (synchronized) voidrun(){}
如何找问题:
1:明确那些代码是多线程运行代码
2:明确共享数据
3:明确多线程运行代码中那些语句是操作共享数据的
同步函数用的是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this。所以同步函数使用的锁就是this
如果同步函数被static修饰后,使用的锁是什么?
发现不是this,因为静态方法中也不可以定义this
静态进内存时,内存中没有本类对象。但是一定有该类对应的字节码文件对象。类名.class。该对象类型是class
静态的同步方法使用的是该方法所在类的字节码文件对象。类名.class
14-多线程(多线程-单例设计模式-懒汉式)
单例设计模式:
1:饿汉式
Class Single
{
private static final Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s
}
}
2:懒汉式(请写出一个延迟加载的单例设计模式示例)
Class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null) //避免每次都判断锁,只有当对象为null时才判断
{
synchronized(Single.class)
{
if(s==null) //如果一个线程跑到第一个if后死了,另一个线程进来创建了对象释放了锁,然后那个线程醒了,进来后还要判断
{
s = new Single();
}
}
}
return s
}
}
问:1、懒汉式和饿汉式有什么不同?
懒汉式的特点在于延迟加载(实例的延迟加载)
2‘懒汉式延迟加载有没有问题?怎么解决?
有,如果多线程访问时存在安全问题;可以加同步解决synchronized
3、加同步的方式?
用同步函数和同步代码块都行。稍微有些低效(每次要判断锁),利用双重判断的形式能解决效 率问题。
4、加同步的时候使用的锁是哪一个?
是该类所属的字节码文件对象
15-多线程(多线程-死锁)
死锁:同步中嵌套同步
public void run()
{
if(flag)
{
while(true)
{
synchronized (ob1)
{
System.out.println("ifob1");
synchronized (ob2)
{
try {wait();}catch(InterruptedException e) {}
System.out.println("ifob2");
}
}
}
}
else
{
while(true)
{
synchronized (ob2)
{
System.out.println("ifob2");
synchronized (ob1)
{
try {wait();}catch(InterruptedException e) {}
System.out.println("ifob1");
}
}
}
}
}
Day12——多线程
01-多线程(线程间通信-示例代码)
思考:一:wait(), notify() ,notify All(),用来操作线程为什么定义在了Object类中?
1:这些方法存在于同步中;
2:使用这些方法时必须要标示所属的同步锁;
3:锁可以是任意对象,所以任意对象调用的方法一定定义在Object中
二: wait() sleep() 有什么区别?
wait() :释放资源,释放锁
sleep() :释放资源,不释放锁
1、线程间通讯:
其实就是多个线程在操作同一个资源;但是操作的动作不同(比如一个放,一个拿)
03-多线程(线程间通信-等待唤醒机制) 等一个线程做完后才唤醒另一个线程
1、wait() notify() notifyAll()
都使用在同步中,因为要对持有监视器(锁)的线程操作;所以要使用在同步中,只有同步才具有锁
2、为什么这些操作现成的方法要定义在Object类中呢?
因为 这些方法在操作同步中线程时,都必须要标示他们所操作线程持有的锁。只有同一个锁上的被等待的线程,可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中
/*
* 线程间的通信:其实就是多个线程在操作同一个资源,但是操作动作不同
* 等待唤醒机制:当一个线程在执行时,要等他完全执行完再去唤醒另一个线程
*/
class Res
{
private Stringname;
private Stringsex;
private boolean flag = false;
public synchronized void set(Stringname,String sex)
{
if(flag)
try{this.wait();}catch(Exception e){}
this.name = name;
this.sex = sex;
flag = true;
this.notify(); //锁调用
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(name+"^^"+sex);
flag = false;
this.notify();
}
}
class Input implements Runnable
{
private Resr;
Input(Resr)
{
this.r = r;
}
public void run()
{
int x=0;
while(true)
{
if(x==0)
{
r.set("mike","nan");
}
else
{
r.set("丽丽","女");
}
x=(x+1)%2;
}
}
}
class Output implements Runnable
{
private Resr;
Output(Resr)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
}
public class InputOutputDemo
{
public static void main(String[] args)
{
Resr = new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
}
}
05-多线程(线程间通信-生产者消费者)
对于多个生产者和消费者,定义while判断标记:让被唤醒的线程再一次判断标记
定义notifyAll,因为要唤醒对方线程。如果只用notify,容易出现只唤醒本方线程,导致程序中的所有线程都等待
//多个生产者生产,多个消费者消费
public classProducerConsumerDemo
{
public static void main(String[]args)
{
Resourcer = new Resource();
Producerpro = new Producer(r);
Consumercon = new Consumer(r);
Threadt1= new Thread(pro);
Threadt2= new Thread(pro);
Thread t3 = new Thread(con);
Thread t4= new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//商品类
class Resource
{
private Stringname;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)
{
while(flag) //为什么用while不用if,因为让醒的线程再去判断标记,这样会避免数据错乱
try{this.wait();}catch(Exception e){}
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"^生产者^"+this.name);
flag = true;
//为什么不用this.notify()因为他只会唤醒线程池中第一个线程,
//,但是线程醒后又去判断标记,容易出现线程全部等待,但不是死锁
this.notifyAll();
}
public synchronized void out()
{
while(!flag)
try{wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"^消费者^^^^"+this.name);
flag = false;
this.notifyAll();
}
}
//生产者
class Producerimplements Runnable
{
private Resourceres;
Producer(Resourceres)
{
this.res = res;
}
public void run()
{
while(true)
{
res.set("+商品+");
}
}
}
//消费者
class Consumerimplements Runnable
{
private Resourceres;
Consumer(Resourceres)
{
this.res = res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
06-多线程(线程间通信-生产者消费者JDK5.0升级版)
Java util.concurrent locks包
JDK1.5中提供了线程升级解决方案。
将同步synchronized替换成现实Lock操作;
将Object中的wait,notify,notifyAll替换成了Condition对象,该对象可以Lock锁,进行获取;
该实例中实现了本方只能唤醒对方的操作。
importjava.util.concurrent.locks.*;
//多个生产者生产,多个消费者消费
public class ProducerConsumer_2
{
public static void main(String[]args)
{
Resource_2r = new Resource_2();
Producer_2pro = new Producer_2(r);
Consumer_2con = new Consumer_2(r);
Threadt1= new Thread(pro);
Threadt2= new Thread(pro);
Thread t3 = new Thread(con);
Thread t4= new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//商品类
class Resource_2
{
private Stringname;
private int count = 1;
private boolean flag = false;
private Locklock = new ReentrantLock();
private Conditioncondition_pro = lock.newCondition();
private Conditioncondition_con = lock.newCondition();
public void set(String name) throwsInterruptedException
{
lock.lock();//拿到锁
try
{
while(flag) //为什么用while不用if,因为让醒的线程再去判断标记,这样会避免数据错乱
condition_pro.await(); //让生产者等待
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"^生产者^"+this.name);
flag = true;
condition_con.signal();//唤醒消费者,不用唤醒全部线程,这样也可以避免全部等待
}
finally
{
lock.unlock();
}
}
public void out() throwsInterruptedException
{
lock.lock();
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"^消费^^^^"+this.name);
flag = false;
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
}
//生产者
class Producer_2implements Runnable
{
private Resource_2res;
Producer_2(Resource_2res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("+商品+");
}
catch(InterruptedExceptione)
{
}
}
}
}
//消费者
class Consumer_2implements Runnable
{
private Resource_2res;
Consumer_2(Resource_2res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch(InterruptedExceptione)
{
}
}
}
}
07-多线程(停止线程)
stop() 方法已经过时,如何停止线程?
只有一种,run()方法结束。开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run()方法结束,也就是线程结束
特殊情况:当线程处于冻结状态(不是运行状态),就不会读取到标记,线程就不会结束
当没有指定的方式让冻结的线程恢复到运行状态时,需要对冻结进行清除,强制让线程恢复到运行状态,这样就可以操作标记让线程结束,Thread类提供该方法interrupt()
注::::t1.interrupt(); 强制让冻结状态的线程恢复到运行状态,t1冻结的地方会抛出IterruptedException异常,处理该异常时(catch){改变标记},来停止线程
class StopThreadimplements Runnable
{
private boolean flag = true; //判断标记,让线程结束
public synchronized void run()
{
while(flag)
{
try
{
wait();
}
catch(InterruptedExceptione)
{
System.out.println(Thread.currentThread().getName()+"……Exception");
flag =false; //当发生异常,说明调用了interrupt方法强制线程清楚冻结,希望线程停
}
System.out.println(Thread.currentThread().getName()+"……run");
}
}
public void changFlag()
{
flag = false;
}
}
public class StopDemo
{
public static void main(String[]args)
{
StopThreadst = new StopThread();
Threadt1 = new Thread(st);
Threadt2 = new Thread(st);
t1.start();
t2.start();
int num = 0;
while(true)
{
if(num++ == 60)
{
t1.interrupt();//让冻结的线程恢复到运行妆状态
t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"……"+num);
}
System.out.println("over");
}
}
08-多线程(守护线程)(后台线程)
T1.setDaemon(true);将该线程标记为守护线程或用户线程。
当正在运行的线程都是守护线程时,Java虚拟机退出,程序结束。
注意:一个线程被标示为守护线程,并不是他不运行了,他跟其他线程一样运行。只是当前台线程执行完后,这些线程否会结束
09-多线程(Join方法)
Join():当A线程执行到了B线程的.join()方法时,A线程就会让出cpu执行权,等待直到B线程执行完,A才会执行。Join可以用来临时加入线程执行。
注意:碰见了谁的join()方法,就只等谁。等他结束就执行。
t1.start();
t1.join(); //等待t1执行完,才继续执行主线程和其他线程,其他线程在期间是冻结的
t2.start();
t1.start();
t2.start();
t1.join(); //当主线程执行到这里,他会冻结,但是t2不会冻结,他和t1交替执行,主线程等t1执行完了才执行,不管t2有没有执行完
10-多线程(优先级&yield方法)
优先级:t1.setPriority(int newPriority);设置线程优先权,1-10,默认5,越大,抢到cpu执行权机会越大;(Thread.MAX_PRIORITY, MIN_PRIORITY ,NORM_PRIORITY)
yield():暂停当前正在执行的线程,并执行其他线程,Thread.yield();
总结:当程序中需要几个动作需要同时进行时,可以用到线程
用匿名类:1: newThread()
{
Public void run(){}
}.start();
2: Runnable r = new Runnable()
{
Public void run(){}
} ;
new Thread(r).start();