黑马程序员---------笔记整理(java基础八-----多线程)
2012-05-22 00:55
716 查看
---------------------- android培训、java培训、期待与您交流! ----------------------
今天有点晕晕的,说不清的感觉,总觉得时间有点紧,不大够用。不知道究竟是怎么了。慢慢来吧,饭总得一口口吃。尽力!!!来吧,让暴风雨来得更猛烈些吧。
1.1 线程的优势
1.2线程的弊端
1.3 线程和进程的区别
1.4多线程的目的
1.5计算机执行任务的原理
1.6 创建线程的两种方式
1.7 线程的安全问题产生的原因
1.9 线程状态图解
2.0 锁
2.1 死锁
2.2 延迟加载同步代码块儿
2.3 线程间通讯
2.4 同步
2.5 其他
多线程
进程:正在运行中的程序。
线程:就是进程中一个负责程序执行的控制单元(执行路径)
1.1 线程的优势
多线程好处:解决了多部分同时运行的问题
1.2线程的弊端
多线程的弊端:线程太多回到效率的降低,因为线程的执行依靠的是CPU的来回切换。 1.3 线程和进程的区别
一个进程中可以多执行路径,称为多线程。
一个进程中至少要有一个线程。
了解:
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。如果有兴趣深入的话,我建议你们看看《现代操作系统》或者《操作系统的设计与实现》。对就个问题说得比较清楚。
1.4多线程的目的
开启多个线程是为了同时运行多部分代码,每一个线程都有自己运行的
内容,这个内容称为线程要执行的任务
1.5计算机执行任务的原理
应用程序的执行都是cpu在做着快速的切换完成的,而且切换是随机的 1.6 创建线程的两种方式
1.继承Thread。
继承Thread类;
覆盖Thread类中的run方法;
直接创建Threadde子类对象创建线程;
调用start方法开启线程并调用线程的任务run方法。
另外可以通过Thread的getName()获取线程的名称。
主要代码:
class ThreadDemo extends Thread
{
public void run()
{
for (int i=0;i<10;i++)
{
System.out.println("线程"+getName()+"正在运行:"+i);
}
}
}
class Demo
{
public static void main(String args[])
{
ThreadDemo a=new ThreadDemo();
ThreadDemo b=new ThreadDemo();
a.start();
b.start();
}
}
2.实现runnable接口
定义类实现Runnable接口;
覆盖接口的run的方法,将线程的任务代码封装到run方法中;
通过Thread类创建线程对象,将Runnabl接口的子类对象作为Thread类的构造函数的参数进行传递
(原因:线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务)。
调用线程对象的start方法开启线程。
主要代码:
class RunnableDemo implements Runnable //实现Runnable
{
public void run()
{
show();
}
private void show()
{
for (int i=1;i<10 ;i++ )
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
class Demo
{
public static void main(String[] args)
{
RunnableDemo a=new RunnableDemo();
Thread b=new Thread(a); //将任务封装成对象,将其传入线程中
Thread b1=new Thread(a);
b.start();
b1.start();
}
}
总结:两种方法的比较:实现Runnable接口,将线程的任务从线程的子类中分离出来的,进行了单独的封装,按照面向对象的思想将任务的封装成对象,避免了java中单继承的局限性。(创建线程第二种方式较好)
1.7 线程的安全问题产生的原因
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
解决思路:就是将多条操作共享的线程代码封装起来,当有线程在执行这些代码的时候,其他线程就不可以参与运算,执行完以后,
其他的线程才 可以执行。
java中的体现:
(同步代码块,解决线程的安全问题,但是降低了效率。因为同步外的线程 都会判断同步锁,同步的前提:同步中必须有有多
个线程并使用用一个锁。)
synchronized(对象)
{
需要被同步的代码;
}
主要代码:
class Test implements Runnable
{
private int num=50;
public void run()
{
while(num>0)//线程每循环一次,释放cpu的执行权,进来再次判断。
{
synchronized(this)//一次只能有一个线程进来售票
{
if(num>0)
System.out.println(Thread.currentThread().getName()+"正在出售:"+num--);
}
}
}
}
class TestDemo
{
public static void main(String[] args)
{
Test a=new Test();
Thread t1=new Thread(a);
Thread t2=new Thread(a);
t1.start();
t2.start();
}
}
2.4 同步
同步的好处:解决了线程的安全问题
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁
同步的前提:同步中必须有多个线程并使用同一个锁。
同步的表现:多个线程运行到同步时,谁具备执行权,其他线程就不参与共享数据的操作。
同步代码块儿和同步函数区别:
同步函数的锁是固定的this。
同步代码块的锁是任意的对象。
建议使用同步代码块。
静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前 类名.class表示。
1.9 线程状态图解
分析:
状态一,创建,使用start()开启线程。
状态二,运行(具备着执行资格,具备着执行权)
状态三,冻结(释放执行权,同时释放执行资格)
从运行到冻结的方式:
sleep(time),sleep(time)时间到,进入临时阻塞状态(具备着执行资格,但是不具备执行权,正在等待执行权)
wait()线程等待,notify()线程唤醒,进入临时阻塞状态。
状态四,消亡
从运行到消亡的方式:
stop()中止线程;
run()方法结束,线程的任务结束。
2.0 锁
锁可以方便的实现资源的封锁,用来控制对竞争资源并发访问的控制
2.1 死锁
同步代码块之间的嵌套。就是有两把锁,你那我需要的那把,我持有你需要的那把,而我们谁也不释放该锁,这样
我们都不能进去,在那等待。
主要代码:
class DeadLock implements Runnable
{
public boolean flag=false;
public void run()
{
if (!flag)
{ while(true)
{
synchronized(this)
{
synchronized(DeadLock.class)
{
System.out.println("true");
}
}
}
}
else
{
while(true)
{
synchronized(DeadLock.class)
{
synchronized(this)
{
System.out.println("false");
}
}
}
}
}
}
class Test
{
public static void main(String[] args)
{
DeadLock a=new DeadLock ();
Thread t1=new Thread(a);
Thread t2=new Thread(a);
t1.start();
try{ Thread.sleep(10);}catch(Exception e){}
a.flag=true;
t2.start();
}
}
2.2 延迟加载同步代码块儿
/*单例设计模式:
//饿汉式。
/*
class Single
{
private
static final Single s = new Single();
private
Single(){}
public
static Single getInstance()
{
return
s;
}
}
//懒汉式,请给我写个延迟加载的单例设计模式。
/*
区别:
懒汉式特点是实例的延迟加载。多线程访问时回出现安全问题。
可以加同步来解决。可以用同步代码块或者同步函数。有些低效,
用双重判断可以效率问题。同步的时候,用的锁是,该类所属的字节码文件对象。
*/
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if (s==null)
{
s = new Single();
}
}
}
return s;
}
}
class SingleDmeo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
2.3 线程间通讯
多线程在处理同一资源,但是任务却不同。
1.1 等待唤醒机制:
涉及的方法:
1, wait(); 是为了让线程处于冻结状态,释放CPU执行权和执行资格,被wait的线程会被存储到线程池中。
2, notify(); 是用于唤醒线程池中的一个线程(任意的线程)。
3, notifyAll(); 是用于唤醒线程池中的所有线程。
这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。必须要明确到底操作的是哪个锁上的线程。
为什么需要唤醒机制:
有时候我们多个线程在操作同一数据时,需要有一个先后顺序,当其中一个线程在操作该数据时,其他线程不能操作该数据,
这时候可能就会涉及到冻结和唤醒机制。
1.2 为什么操作线程的方法wait
notify notifyAll 定义在Object类中?
因为这些方法是监视器的方法,监视器其实就是锁,锁可以是任意的对象,任意的对象调用的方法一定定义在Object类当中。
1 .3 线程间通讯最重要的机制:
等待唤醒机制。
wait():让当前线程处于冻结状态,当前线程就被存储到了线程池中。
notify():唤醒线程池中的任意一个线程。让该线程恢复到运行状态,会具备cpu的执行资格。
notifyAll(): 唤醒线程池中的所有等待的线程,具备cpu的执行资格。
1.4 等待唤醒机制中,最常见的体现就是生产者消费者问题:
发现两个问题:
1,出现了错误的数据。是因为多生产和多消费的时候,被唤醒的线程没有再次判断标记就执行了。
解决是将if判断变成while判断。
2,发现有了while判断后,死锁了。
因为本方线程唤醒的有可能还是本方线程。所以导致了死锁。
解决:本方必须唤醒对方才有效。notify只能唤醒一个,还不确定。所以干脆唤醒全部,肯定包含对方,
至于被唤醒的本方,会判断标记是否继续等待。
示例代码:
class ThreadDemo4
{
public static void main(String [] args)
{
Resource r = new Resource();
Shengchan s = new Shengchan(r);
Xiaofei x = new Xiaofei(r);
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
Thread t3 = new Thread(x);
Thread t4 = new Thread(x);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)
{
if(flag)
try{this.wait();}catch(InterruptedException e){}
this.name = name+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者...."+this.name);
flag = true;
notify();
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".........消费者........."+this.name);
flag = false;
notify();
}
}
class Shengchan implements Runnable
{
Resource r;
Shengchan(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
r.set("烤鸭");
}
}
class Xiaofei implements Runnable
{
Resource r;
Xiaofei(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
r.out();
}
}
编译后:
造成了线程的不安全的问题。
notify唤醒的是不确定的一其中的一个线程,造成了生产出的烤鸭没有被消费。
解决多生产多消费的线程安全问题:
示例代码:
class ThreadDemo4
{
public static void main(String [] args)
{
Resource r = new Resource();
Shengchan s = new Shengchan(r);
Xiaofei x = new Xiaofei(r);
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
Thread t3 = new Thread(x);
Thread t4 = new Thread(x);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)
{
while(flag) //这里写while循环是为了当线程每次醒后再判断一次标记。
try{this.wait();}catch(InterruptedException e){}
this.name = name+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者...."+this.name);
flag = true;
notifyAll();
}
public synchronized void out()
{
while(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".........消费者........."+this.name);
flag = false;
notifyAll();
}
}
class Shengchan implements Runnable
{
Resource r;
Shengchan(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
r.set("烤鸭");
}
}
class Xiaofei implements Runnable
{
Resource r;
Xiaofei(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
r.out();
}
}
1.5 同步代码块儿
同步代码块,对于锁的操作是隐式的
JDK1.5以后将同步和锁封装成了对象。并将操作锁的隐式方式定义到了对象中,将隐式动作编程了显示动作。
1.6 Lock接口
出现替代了同步代码块或者同步函数。将同步的隐式锁操作编程了显示锁操作。 同事更为灵活。可以一个锁上加上多组监视器。
lock(); 获取锁。
unlock();释放锁,通常需要定义finally代码块中。
JDK1.5版本后,对多线程中的内部细节进行了升级改良。
在java.util.concurrent.locks包中提供了一个Lock接口。
Lock接口中提供了 lock()获取锁 unlock释放锁的操作。
Lock接口更符合面向对象的思想,将锁这种事物封装成了对象。
public void run()
{
synchronized(obj)
{//获取锁。
code...
//释放锁。
}
}
但是对于释放和获取锁的操作,都是隐式的。
JDK1.5后,就有了新的方法,将锁封装成了对象。因为释放锁和获取锁动作,锁自己最清楚。
锁对象的类型就是Lock接口。
并提供了,显示的对锁的获取和释放的操作方法。
Lock lock;
public void run()
{
try
{
lock.lock();//获取锁。
code...throw ...
}
finally
{
lock.unlock();//释放锁.
}
}
Lock接口替代了synchronized .
Condition替代了Object类中监视器方法 wait
notify notifyAll。、
将监视器方法单独封装成了Condition对象。而且一个锁上可以组合多组监视器对象。
实现了多生产者多消费者时,本方只唤醒对方中一个的操作,提高效率。
1.7Condition接口
出现替代了Object中的wait
notify notifyAll方法。
await(),signal(); signalAll();
将这些监视器方法单独进行封装,变成了Condition监视器对象。
可以任意锁进行组合。
await(); 睡眠
signal();signalAll(); 唤醒
使用为一般是生产者是被消费者唤醒,消费者是被生产者唤醒。
1.8 停止线程:
1,stop方法
2,run方法结束。
stop方法已经过时。 所以只剩下一种办法:线程执行的代码结束,线程会自动终止。
1,run方法中通常都有循环语句,所以只要让循环结束即可,所以只要控制住循环的条件,最简单的方式就是定义标记。
怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
但是如果线程处于了冻结状态,无法读取标记,如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。但是强制动作会发
terruptedException,记得要处理。
示例代码:
class StopThread implements Runnable
{
private boolean flag = true;
public synchronized void run(){
while(flag){
try {
wait();//t0 t1
}catch (InterruptedException
e){
System.out.println(Thread.currentThread().getName()+"....."+e);
flag = false;
}
System.out.println(Thread.currentThread().getName()+"......++++");
}
}
public void setFlag(){
flag = false;
}
}
class StopThreadDemo
{
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.setDaemon(true);
t2.start();
int num = 1;
for(;;){
if(++num==50){
//st.setFlag();
t1.interrupt();
//t2.interrupt();
break;
}
System.out.println("main...."+num);
}
System.out.println("over");
}
}
临时加入一个线程运算时可以使用join方法。
面试题
class Test implements Runnable
{
public void run(Thread t)
{}
}
//如果错误 错误发生在哪一行?错误在第一行,应该被abstract修饰
class ThreadTest
{
public static void main(String[] args)
{
new Thread(new Runnable(){
public void run(){
System.out.println("runnable run");
}}){
public void run(){
System.out.println("subThread run");
}
}.start();
打印的是subThread run 运行以子类的为主,如果子类里面没有这个run()方法的话,就以任务线程的run为主,如果都没有的话,以父类自己的为主。
总结:
wait和sleep的区别:
wait可以指定时间也可以不指定.
sleep必须指定时间。
在同步中时,对于CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
wait方法,必须定义在同步中,sleep不一定。
-
--------------------- android培训、java培训、期待与您交流! ----------------------
今天有点晕晕的,说不清的感觉,总觉得时间有点紧,不大够用。不知道究竟是怎么了。慢慢来吧,饭总得一口口吃。尽力!!!来吧,让暴风雨来得更猛烈些吧。
1.1 线程的优势
1.2线程的弊端
1.3 线程和进程的区别
1.4多线程的目的
1.5计算机执行任务的原理
1.6 创建线程的两种方式
1.7 线程的安全问题产生的原因
1.9 线程状态图解
2.0 锁
2.1 死锁
2.2 延迟加载同步代码块儿
2.3 线程间通讯
2.4 同步
2.5 其他
多线程
进程:正在运行中的程序。
线程:就是进程中一个负责程序执行的控制单元(执行路径)
1.1 线程的优势
多线程好处:解决了多部分同时运行的问题
1.2线程的弊端
多线程的弊端:线程太多回到效率的降低,因为线程的执行依靠的是CPU的来回切换。 1.3 线程和进程的区别
一个进程中可以多执行路径,称为多线程。
一个进程中至少要有一个线程。
了解:
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。如果有兴趣深入的话,我建议你们看看《现代操作系统》或者《操作系统的设计与实现》。对就个问题说得比较清楚。
1.4多线程的目的
开启多个线程是为了同时运行多部分代码,每一个线程都有自己运行的
内容,这个内容称为线程要执行的任务
1.5计算机执行任务的原理
应用程序的执行都是cpu在做着快速的切换完成的,而且切换是随机的 1.6 创建线程的两种方式
1.继承Thread。
继承Thread类;
覆盖Thread类中的run方法;
直接创建Threadde子类对象创建线程;
调用start方法开启线程并调用线程的任务run方法。
另外可以通过Thread的getName()获取线程的名称。
主要代码:
class ThreadDemo extends Thread
{
public void run()
{
for (int i=0;i<10;i++)
{
System.out.println("线程"+getName()+"正在运行:"+i);
}
}
}
class Demo
{
public static void main(String args[])
{
ThreadDemo a=new ThreadDemo();
ThreadDemo b=new ThreadDemo();
a.start();
b.start();
}
}
2.实现runnable接口
定义类实现Runnable接口;
覆盖接口的run的方法,将线程的任务代码封装到run方法中;
通过Thread类创建线程对象,将Runnabl接口的子类对象作为Thread类的构造函数的参数进行传递
(原因:线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务)。
调用线程对象的start方法开启线程。
主要代码:
class RunnableDemo implements Runnable //实现Runnable
{
public void run()
{
show();
}
private void show()
{
for (int i=1;i<10 ;i++ )
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
class Demo
{
public static void main(String[] args)
{
RunnableDemo a=new RunnableDemo();
Thread b=new Thread(a); //将任务封装成对象,将其传入线程中
Thread b1=new Thread(a);
b.start();
b1.start();
}
}
总结:两种方法的比较:实现Runnable接口,将线程的任务从线程的子类中分离出来的,进行了单独的封装,按照面向对象的思想将任务的封装成对象,避免了java中单继承的局限性。(创建线程第二种方式较好)
1.7 线程的安全问题产生的原因
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。
解决思路:就是将多条操作共享的线程代码封装起来,当有线程在执行这些代码的时候,其他线程就不可以参与运算,执行完以后,
其他的线程才 可以执行。
java中的体现:
(同步代码块,解决线程的安全问题,但是降低了效率。因为同步外的线程 都会判断同步锁,同步的前提:同步中必须有有多
个线程并使用用一个锁。)
synchronized(对象)
{
需要被同步的代码;
}
主要代码:
class Test implements Runnable
{
private int num=50;
public void run()
{
while(num>0)//线程每循环一次,释放cpu的执行权,进来再次判断。
{
synchronized(this)//一次只能有一个线程进来售票
{
if(num>0)
System.out.println(Thread.currentThread().getName()+"正在出售:"+num--);
}
}
}
}
class TestDemo
{
public static void main(String[] args)
{
Test a=new Test();
Thread t1=new Thread(a);
Thread t2=new Thread(a);
t1.start();
t2.start();
}
}
2.4 同步
同步的好处:解决了线程的安全问题
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁
同步的前提:同步中必须有多个线程并使用同一个锁。
同步的表现:多个线程运行到同步时,谁具备执行权,其他线程就不参与共享数据的操作。
同步代码块儿和同步函数区别:
同步函数的锁是固定的this。
同步代码块的锁是任意的对象。
建议使用同步代码块。
静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前 类名.class表示。
1.9 线程状态图解
分析:
状态一,创建,使用start()开启线程。
状态二,运行(具备着执行资格,具备着执行权)
状态三,冻结(释放执行权,同时释放执行资格)
从运行到冻结的方式:
sleep(time),sleep(time)时间到,进入临时阻塞状态(具备着执行资格,但是不具备执行权,正在等待执行权)
wait()线程等待,notify()线程唤醒,进入临时阻塞状态。
状态四,消亡
从运行到消亡的方式:
stop()中止线程;
run()方法结束,线程的任务结束。
2.0 锁
锁可以方便的实现资源的封锁,用来控制对竞争资源并发访问的控制
2.1 死锁
同步代码块之间的嵌套。就是有两把锁,你那我需要的那把,我持有你需要的那把,而我们谁也不释放该锁,这样
我们都不能进去,在那等待。
主要代码:
class DeadLock implements Runnable
{
public boolean flag=false;
public void run()
{
if (!flag)
{ while(true)
{
synchronized(this)
{
synchronized(DeadLock.class)
{
System.out.println("true");
}
}
}
}
else
{
while(true)
{
synchronized(DeadLock.class)
{
synchronized(this)
{
System.out.println("false");
}
}
}
}
}
}
class Test
{
public static void main(String[] args)
{
DeadLock a=new DeadLock ();
Thread t1=new Thread(a);
Thread t2=new Thread(a);
t1.start();
try{ Thread.sleep(10);}catch(Exception e){}
a.flag=true;
t2.start();
}
}
2.2 延迟加载同步代码块儿
/*单例设计模式:
//饿汉式。
/*
class Single
{
private
static final Single s = new Single();
private
Single(){}
public
static Single getInstance()
{
return
s;
}
}
//懒汉式,请给我写个延迟加载的单例设计模式。
/*
区别:
懒汉式特点是实例的延迟加载。多线程访问时回出现安全问题。
可以加同步来解决。可以用同步代码块或者同步函数。有些低效,
用双重判断可以效率问题。同步的时候,用的锁是,该类所属的字节码文件对象。
*/
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if (s==null)
{
s = new Single();
}
}
}
return s;
}
}
class SingleDmeo
{
public static void main(String[] args)
{
System.out.println("Hello World!");
}
}
2.3 线程间通讯
多线程在处理同一资源,但是任务却不同。
1.1 等待唤醒机制:
涉及的方法:
1, wait(); 是为了让线程处于冻结状态,释放CPU执行权和执行资格,被wait的线程会被存储到线程池中。
2, notify(); 是用于唤醒线程池中的一个线程(任意的线程)。
3, notifyAll(); 是用于唤醒线程池中的所有线程。
这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。必须要明确到底操作的是哪个锁上的线程。
为什么需要唤醒机制:
有时候我们多个线程在操作同一数据时,需要有一个先后顺序,当其中一个线程在操作该数据时,其他线程不能操作该数据,
这时候可能就会涉及到冻结和唤醒机制。
1.2 为什么操作线程的方法wait
notify notifyAll 定义在Object类中?
因为这些方法是监视器的方法,监视器其实就是锁,锁可以是任意的对象,任意的对象调用的方法一定定义在Object类当中。
1 .3 线程间通讯最重要的机制:
等待唤醒机制。
wait():让当前线程处于冻结状态,当前线程就被存储到了线程池中。
notify():唤醒线程池中的任意一个线程。让该线程恢复到运行状态,会具备cpu的执行资格。
notifyAll(): 唤醒线程池中的所有等待的线程,具备cpu的执行资格。
1.4 等待唤醒机制中,最常见的体现就是生产者消费者问题:
发现两个问题:
1,出现了错误的数据。是因为多生产和多消费的时候,被唤醒的线程没有再次判断标记就执行了。
解决是将if判断变成while判断。
2,发现有了while判断后,死锁了。
因为本方线程唤醒的有可能还是本方线程。所以导致了死锁。
解决:本方必须唤醒对方才有效。notify只能唤醒一个,还不确定。所以干脆唤醒全部,肯定包含对方,
至于被唤醒的本方,会判断标记是否继续等待。
示例代码:
class ThreadDemo4
{
public static void main(String [] args)
{
Resource r = new Resource();
Shengchan s = new Shengchan(r);
Xiaofei x = new Xiaofei(r);
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
Thread t3 = new Thread(x);
Thread t4 = new Thread(x);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)
{
if(flag)
try{this.wait();}catch(InterruptedException e){}
this.name = name+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者...."+this.name);
flag = true;
notify();
}
public synchronized void out()
{
if(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".........消费者........."+this.name);
flag = false;
notify();
}
}
class Shengchan implements Runnable
{
Resource r;
Shengchan(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
r.set("烤鸭");
}
}
class Xiaofei implements Runnable
{
Resource r;
Xiaofei(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
r.out();
}
}
编译后:
造成了线程的不安全的问题。
notify唤醒的是不确定的一其中的一个线程,造成了生产出的烤鸭没有被消费。
解决多生产多消费的线程安全问题:
示例代码:
class ThreadDemo4
{
public static void main(String [] args)
{
Resource r = new Resource();
Shengchan s = new Shengchan(r);
Xiaofei x = new Xiaofei(r);
Thread t1 = new Thread(s);
Thread t2 = new Thread(s);
Thread t3 = new Thread(x);
Thread t4 = new Thread(x);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)
{
while(flag) //这里写while循环是为了当线程每次醒后再判断一次标记。
try{this.wait();}catch(InterruptedException e){}
this.name = name+count;
count++;
System.out.println(Thread.currentThread().getName()+"...生产者...."+this.name);
flag = true;
notifyAll();
}
public synchronized void out()
{
while(!flag)
try{this.wait();}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".........消费者........."+this.name);
flag = false;
notifyAll();
}
}
class Shengchan implements Runnable
{
Resource r;
Shengchan(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
r.set("烤鸭");
}
}
class Xiaofei implements Runnable
{
Resource r;
Xiaofei(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
r.out();
}
}
1.5 同步代码块儿
同步代码块,对于锁的操作是隐式的
JDK1.5以后将同步和锁封装成了对象。并将操作锁的隐式方式定义到了对象中,将隐式动作编程了显示动作。
1.6 Lock接口
出现替代了同步代码块或者同步函数。将同步的隐式锁操作编程了显示锁操作。 同事更为灵活。可以一个锁上加上多组监视器。
lock(); 获取锁。
unlock();释放锁,通常需要定义finally代码块中。
JDK1.5版本后,对多线程中的内部细节进行了升级改良。
在java.util.concurrent.locks包中提供了一个Lock接口。
Lock接口中提供了 lock()获取锁 unlock释放锁的操作。
Lock接口更符合面向对象的思想,将锁这种事物封装成了对象。
public void run()
{
synchronized(obj)
{//获取锁。
code...
//释放锁。
}
}
但是对于释放和获取锁的操作,都是隐式的。
JDK1.5后,就有了新的方法,将锁封装成了对象。因为释放锁和获取锁动作,锁自己最清楚。
锁对象的类型就是Lock接口。
并提供了,显示的对锁的获取和释放的操作方法。
Lock lock;
public void run()
{
try
{
lock.lock();//获取锁。
code...throw ...
}
finally
{
lock.unlock();//释放锁.
}
}
Lock接口替代了synchronized .
Condition替代了Object类中监视器方法 wait
notify notifyAll。、
将监视器方法单独封装成了Condition对象。而且一个锁上可以组合多组监视器对象。
实现了多生产者多消费者时,本方只唤醒对方中一个的操作,提高效率。
1.7Condition接口
出现替代了Object中的wait
notify notifyAll方法。
await(),signal(); signalAll();
将这些监视器方法单独进行封装,变成了Condition监视器对象。
可以任意锁进行组合。
await(); 睡眠
signal();signalAll(); 唤醒
使用为一般是生产者是被消费者唤醒,消费者是被生产者唤醒。
1.8 停止线程:
1,stop方法
2,run方法结束。
stop方法已经过时。 所以只剩下一种办法:线程执行的代码结束,线程会自动终止。
1,run方法中通常都有循环语句,所以只要让循环结束即可,所以只要控制住循环的条件,最简单的方式就是定义标记。
怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
但是如果线程处于了冻结状态,无法读取标记,如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。但是强制动作会发
terruptedException,记得要处理。
示例代码:
class StopThread implements Runnable
{
private boolean flag = true;
public synchronized void run(){
while(flag){
try {
wait();//t0 t1
}catch (InterruptedException
e){
System.out.println(Thread.currentThread().getName()+"....."+e);
flag = false;
}
System.out.println(Thread.currentThread().getName()+"......++++");
}
}
public void setFlag(){
flag = false;
}
}
class StopThreadDemo
{
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.setDaemon(true);
t2.start();
int num = 1;
for(;;){
if(++num==50){
//st.setFlag();
t1.interrupt();
//t2.interrupt();
break;
}
System.out.println("main...."+num);
}
System.out.println("over");
}
}
临时加入一个线程运算时可以使用join方法。
面试题
class Test implements Runnable
{
public void run(Thread t)
{}
}
//如果错误 错误发生在哪一行?错误在第一行,应该被abstract修饰
class ThreadTest
{
public static void main(String[] args)
{
new Thread(new Runnable(){
public void run(){
System.out.println("runnable run");
}}){
public void run(){
System.out.println("subThread run");
}
}.start();
打印的是subThread run 运行以子类的为主,如果子类里面没有这个run()方法的话,就以任务线程的run为主,如果都没有的话,以父类自己的为主。
总结:
wait和sleep的区别:
wait可以指定时间也可以不指定.
sleep必须指定时间。
在同步中时,对于CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
wait方法,必须定义在同步中,sleep不一定。
-
--------------------- android培训、java培训、期待与您交流! ----------------------
相关文章推荐
- 黑马程序员---Java基础学习笔记(多线程-后篇)
- 黑马程序员---------笔记整理(java基础九-----String,StringBuffer,StringBuilder)
- 黑马程序员-------Java笔记--------多线程基础练习2题
- 黑马程序员---Java基础学习笔记(多线程-前篇)
- 黑马程序员_java基础_多线程学习笔记
- 黑马程序员_java基础笔记(04)...多线程
- 黑马程序员---------笔记整理(java基础六-----异常)
- 黑马程序员_java基础笔记(04)...多线程
- 黑马程序员-----笔记整理(java基础五-------面向对象--多态)
- 笔记整理(java基础八-----多线程)
- 黑马程序员--- 笔记整理(java基础七-----问题整理)
- 黑马程序员_java基础笔记(04)...多线程
- 黑马程序员_java基础学习笔记10_多线程
- 黑马程序员自学笔记 Java基础<五>---> 多线程
- 黑马程序员--Java基础学习笔记【序列化、多线程】
- 黑马程序员—Java基础学习笔记之多线程
- 黑马程序员-----笔记整理(java基础十----集合)
- 黑马程序员—10—java基础:有关多线程安全的学习笔记和学习心得体会
- 黑马程序员——Java基础-整理笔记
- 黑马程序员 Java基础学习笔记 网络编程