进程:是一个正在执行的程序.
每一个进行执行都有一个执行顺序,该顺序是一个执行路径,或叫一个控制单元.
线程:就是进程中的一个独立控制单元.
线程在控制着进行的执行.
一个进程中至少有一个线程.
java VM启动的时候会有一个进程叫java.exe.
该进程中至少有一个线程在负责java程序的执行.而且这个线程运行的代码存在于main方法中,该线程称之为主线程.
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程.
如何在自定义代码中,自定义一个线程呢?
通过对api的查找,java已经提供了对线程这类事物的描述,就是Tread类
创建线程的第一种方式:继承Thread类.
步骤:
1.继承Thread类.
2.复写Thread类中的run方法.
目的:将自定义代码存储在run方法中,让线程运行.
3.创建该子类的对象,并调用线程的start方法.
该方法有两个作用:启动线程,调用run方法.
创建线程的第二种方式:实现Runnable接口
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法.
将线程要运行的代码存放在该run方法中.
3.通过Thread类建立线程对象.
4.将Runnable解救的子类对象作为实际参数传递给Thread类的构造函数.
为什么要将Runnable接口的子类对象传递给Thread的构造函数.
因为,自定义的run方法所属的对象是Runnable接口的子类对象.
所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象.
5.调用Thread类的start方法开启线程并调用Runnable接口子类的方法.
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性.
在定义线程时,建议使用实现Runnable的方式.
两种方式的区别:
继承Thread:线程代码存放在Thread子类的run方法中.
实现Runnable:线程代码存放在接口子类的run方法中
发现运行结果每一次都不同.
因为多个线程都在获取cpu执行权,cpu执行到谁,谁就运行.
明确一点,在某一时刻,只能有一个程序在运行.(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果.
毕老师总结:
我们可以形象的把多线程的运行行为看做线程在互相抢夺cpu的执行权.
值就是多线程的一个特性:随机性.谁抢到谁执行,至于执行多长,cpu说的算.
为什么要覆盖run方法呢?
Thread类用于描述线程.
该类就定义了一个功能,用于存储线程要运行的代码.该存储功能就是run方法.
也就是Thread类中的run方法,是用于存储线程要运行的代码.
举例代码:
- class Demo extends Thread
- {
- public void run()
- {
- for (int x=0;x<60;x++)
- {
- System.out.println("Demo run--"+x);
- }
- }
- }
- class ThreadDemo
- {
- public static void main(String[] args)
- {
- Demo d = new Demo();
- d.start();//开启线程,并执行该线程的run方法.
- //d.run();//没调用线程,直接调用了run方法
- for (int x=0;x<60;x++)
- {
- System.out.println("Hello World--"+x);
- }
- }
- }
获取线程名称以及对象:
创建两个线程,和主线程交替运行.
原来线程都有自己的默认名称
Thread-编号,该编号从0开始.
Thread.currentThread():获取当前线程对象.
getName():获取线程名称.
设置线程名称:用setName或者构造函数.
局部变量在每一个线程中都有独立的内存空间.
- class DemoA extends Thread
- {
- //private String name;
- DemoA(String name)
- {
- super(name);
- //this.name=name;
- }
- public void run()
- {
- for (int x = 0;x<10;x++)
- {
- System.out.println((Thread.currentThread()==this)+this.getName()+"-run-"+x);
- }
- }
- }
- class ThreadTest
- {
- public static void main(String[] args)
- {
- new Thread().getName();
- DemoA d1 = new DemoA("one---");
- d1.start();
- DemoA d2 = new DemoA("two+++");
- d2.start();
- for (int x = 0;x<10;x++)
- {
- System.out.println("three==="+x+x);
- }
- }
- }
- class Ticket implements Runnable
- {
- private int ticket = 20;
- public void run()
- {
- while(ticket>0)
- {
- System.out.println(Thread.currentThread().getName()+"---"+ticket--);
- }
- }
- }
- class TicketDemo
- {
- public static void main(String[] args)
- {
- Ticket t = new Ticket();;
- new Thread(t).start();
- new Thread(t).start();
- new Thread(t).start();
- new Thread(t).start();
- }
- }
多线程的安全问题--同步代码块
多线程的运行出现了安全问题.
问题的原因:
当多条语句在操作同一个线程共享数据时.一个线程对多条语句只执行一部分,还没有执行完.
另一个线程参与进来执行.导致共享数据的错误.
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完.在执行过程中,其他线程不可以参与执行.
java对于多线程的安全问题提供了专业的解决方式.
就是同步代码块.
synchronized(对象(锁))
{
需要被同步的代码
}
对象如同锁,持有锁的线程可以再同步中执行.
没有持有锁的线程,即使获取了cpu的执行权,也进不去,因为没有锁.
毕老师说synchronized就如同火车上的卫生间---经典
同步的前提!!!!!
1.必须要有两个或两个以上的线程.
2.必须是多个线程使用同一个锁.
必须保证同步中只能有一个线程运行.
好处:解决了多线程的安全问题.
弊端:多个线程需要判断锁,较为消耗资源.
- class Ticket implements Runnable
- {
- private int ticket = 1000;
- Object obj = new Object();
- public void run()
- {
- while(true)
- {
- synchronized(obj)\\obj代表任意对象
- {
- if(ticket>0)
- {
- try{Thread.sleep(10);}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"---"+ticket--);
- }
- }
- }
- }
- }
- class TicketDemo1
- {
- public static void main(String[] args)
- {
- Ticket t = new Ticket();;
- new Thread(t).start();
- new Thread(t).start();
- new Thread(t).start();
- new Thread(t).start();
- }
- }
同步函数:同步代码块可以抽取为同步函数,以简化书写.
同步函数用的那一个锁呢?
函数需要被对象调用.函数都有一个所属对象的引用this
所以同步函数使用的锁是this.
通过该程序进行验证.
使用两个线程来卖票.
一个线程在同步代码块中.
一个线程在同步函数中.
都在执行卖票动作.
见下面代码:
- class Ticket implements Runnable
- {
- private int ticket = 200;
- //Object obj = new Object();
- boolean flag = true;
- public void run()
- {
- if(flag)
- {
- while(true)
- {
- synchronized(this)
- {
- if(ticket>0)
- {
- try{Thread.sleep(10);}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"---code---"+ticket--);
- }
- }
- }
- }
- else
- while(true)
- {
- show();
- }
- }
- public synchronized void show()
- {
- if(ticket>0)
- {
- try{Thread.sleep(10);}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"--show--"+ticket--);
- }
- }
- }
- class ThisLockDemo
- {
- public static void main(String[] args)
- {
- Ticket t = new Ticket();;
- new Thread(t).start();
- try{Thread.sleep(10);}catch(Exception e){}
- t.flag = false;
- new Thread(t).start();
- }
- }
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是this,因为静态方法中也不可以引用this.
静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象.
类名.class,该了对象类型是Class.
静态的同步方法,使用的锁是该方法所在的字节码文件对象.类名.class
- class Ticket implements Runnable
- {
- private static int ticket = 200;
- //Object obj = new Object();
- boolean flag = true;
- public void run()
- {
- if(flag)
- {
- while(true)
- {
- synchronized(Ticket.class)
- {
- if(ticket>0)
- {
- try{Thread.sleep(10);}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"---code---"+ticket--);
- }
- }
- }
- }
- else
- while(true)
- {
- show();
- }
- }
- public static synchronized void show()
- {
- if(ticket>0)
- {
- try{Thread.sleep(10);}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"--show--"+ticket--);
- }
- }
- }
- class StaticLockDemo
- {
- public static void main(String[] args)
- {
- Ticket t = new Ticket();;
- new Thread(t).start();
- try{Thread.sleep(10);}catch(Exception e){}
- t.flag = false;
- new Thread(t).start();
- }
- }
同步的死锁问题:
死锁:2个或2个以上的进程在执行过程中,因为争夺资源而造成的一种互相等待的现象,若无外力作用,他们都无法推进下去,此时系统处于死锁状态,这些永远在互相等待的进程成为死锁进程。
产生原因:同步中嵌套同步,而锁不同;
线程之间的通信
其实就是多个线程在操作同一个资源,
但是操作的动作不同.
wait();
notify();
notifyAll();
都使用在synchronized同步中,因为要对持有监视器(锁)的线程操作.
所以要使用在同步中,因为只有同步才具有监视器(锁).
为什么这些操作线程的方法要定义在Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作的线程持有的锁.
只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒.
不可以对不同锁中的线程进行唤醒.
也就是说,等待和唤醒必须是同一个锁.
而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中.
举例代码:
- //输入输出类的共享资源
- class Share
- {
- String name;
- String sex;
- boolean flag = false;
- //供输入调用的方法
- public synchronized void set(String name,String sex)
- {
- if (flag)
- try{this.wait();}catch(Exception e){}
- this.name = name;
- this .sex = sex;
- flag = true;
- this.notify();
- }
- //供输出调用的方法
- public synchronized void print()
- {
- if (!flag)
- try{this.wait();}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+name+"----"+sex);
- flag = false;
- this.notify();
- }
- }
- class InPut implements Runnable
- {
- Share s;
- int x = 0;
- InPut(Share s)
- {
- this.s = s;
- }
- public void run()
- {
- inMethod();
- }
- public void inMethod()
- {
- while (true)
- {
- if(x==0)
- s.set("toby","man") ;
- else
- s.set("肖华","女女女女") ;
- x = (x+1)%2;
- }
- }
- }
- class OutPut implements Runnable
- {
- Share s;
- OutPut(Share s)
- {
- this.s = s;
- }
- public void run()
- {
- outMethod();
- }
- public void outMethod()
- {
- while (true)
- {
- s.print();
- }
- }
- }
- class DuoDemo
- {
- public static void main(String[] args)
- {
- Share s = new Share();
- InPut i = new InPut(s);
- OutPut o = new OutPut(s);
- new Thread(i).start();
- new Thread(o).start();
- }
- }
- //生产者和消费者的共享资源类
- class Resource
- {
- private String name;
- private int count = 1;
- //定义一个boolean型的标记变量;
- boolean flag = false;
- public synchronized void set(String name)
- {
- while (flag)
- try{wait();}catch(Exception e){}
- this.name = name + count++;
- System.out.println(Thread.currentThread().getName()+"----生产者----"+this.name);
- flag = true;
- this.notifyAll();
- }
- public synchronized void get()
- {
- while (!flag)
- try{wait();}catch(Exception e){}
- System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
- flag = false;
- this.notifyAll();
- }
- }
- class Pro implements Runnable
- {
- Resource r;
- Pro(Resource r)
- {
- this.r = r;
- }
- public void run()
- {
- while (true)
- r.set("商品");
- }
- }
- class Conn implements Runnable
- {
- Resource r;
- Conn(Resource r)
- {
- this.r = r;
- }
- public void run()
- {
- while (true)
- r.get();
- }
- }
- class ProConDemo1
- {
- public static void main(String[] args)
- {
- Resource s = new Resource();
- new Thread(new Pro(s)).start();//0
- new Thread(new Conn(s)).start();//1
- new Thread(new Pro(s)).start();//2
- new Thread(new Conn(s)).start();//3
- }
- }
- //导入jdk1.5版的lock类
- import java.util.concurrent.locks.*;
- //定义一个生产者消费者共享资源类
- class Resource
- {
- private String name;
- private int count = 1;
- boolean flag = false;
- //定义一个锁
- Lock l = new ReentrantLock();
- //定义2个锁的对象
- Condition conset = l.newCondition();
- Condition conout = l.newCondition();
- public void set(String name)throws InterruptedException
- {
- l.lock ();//取代synchronized
- try
- {
- if(flag)
- conset.await();//取代wait
- this.name = name + count++;
- System.out.println(Thread.currentThread().getName()+"_____生产者____"+this.name);
- flag = true;
- conout.signal();//取代notify,唤醒调用out方法的线程
- }
- finally
- {
- l.unlock();//取代synchronized
- }
- }
- public void out()throws InterruptedException
- {
- l.lock ();
- try
- {
- if(!flag)
- conout.await();
- System.out.println(Thread.currentThread().getName()+"消费者"+this.name);
- flag = false;
- conset.signal();//唤醒调用set方法的线程
- }
- finally
- {
- l.unlock();
- }
- }
- }
- class Pro implements Runnable
- {
- Resource r;
- Pro(Resource r)
- {
- this.r = r;
- }
- public void run()
- {
- while (true)
- try
- {
- r.set("商品");
- }
- catch (InterruptedException i)
- {
- System.out.println("呵呵");
- }
- }
- }
- class Conn implements Runnable
- {
- Resource r;
- Conn(Resource r)
- {
- this.r = r;
- }
- public void run()
- {
- while (true)
- try
- {
- r.out();
- }
- catch (InterruptedException i)
- {
- System.out.println("呵呵");
- }
- }
- }
- class ProConDemo2
- {
- public static void main(String[] args)
- {
- Resource r = new Resource();
- Pro p = new Pro(r);
- Conn c = new Conn(r);
- new Thread(p).start();
- new Thread(p).start();
- new Thread(c).start();
- new Thread(c).start();
- }
- }
如何停止线程?
只有一种,run方法结束.
开启多线程运行,运行代码通常是循环结构.
只要控制住循环,就可以让run方法结束,也就是线程结束.
特殊情况:
当线程处于冻结状态.
就不会读取到标记.那么线程就不会结束.
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除.
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束.
Thread类中提供了该方法 interrupt();
- class StopThread implements Runnable
- {
- boolean flag = true;
- public synchronized void run()
- {
- while(flag)
- {
- try
- {
- wait();
- }
- catch (InterruptedException i)
- {
- System.out.println(Thread.currentThread().getName()+"---Exception!");
- flag = false;
- }
- System.out.println(Thread.currentThread().getName()+"---stop");
- }
- }
- public void changeFlag()
- {
- 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.start();
- int num = 0;
- while (true)
- {
- if(num++ == 60)
- {
- //st.changeFlag();
- t1.interrupt();
- t2.interrupt();
- break;
- }
- System.out.println(Thread.currentThread().getName()+"---"+num);
- }
- System.out.println(Thread.currentThread().getName()+"---over");
- }
- }
守护线程(后台线程)
Thread类中的setDaemon(boolean on)方法.
on如果为true,则将该线程标记为守护线程(后台线程).
将该线程标记为守护线程或用户线程.当正在运行的线程都是守护线程时,Java 虚拟机退出.
该方法必须在启动线程前调用.
这可能抛出 SecurityException(在当前线程中).
join方法
当A线程执行到了B线程的join()方法时,A线程就会等待.等B线程都执行完,A才会执行.
join可以用来临时加入线程执行.
举例代码:
- class Demo implements Runnable
- {
- public void run()
- {
- for(int x=0; x<70; x++)
- {
- System.out.println(Thread.currentThread().getName()+"....."+x);
- }
- }
- }
- class JoinDemo
- {
- public static void main(String[] args)throws Exception
- {
- Demo d = new Demo();
- Thread t1 = new Thread(d);
- Thread t2 = new Thread(d);
- t1.start();
- t2.start();
- t1.join();//等待t1终止
- for (int x=0; x<80; x++ )
- {
- System.out.println("main....."+x);
- }
- System.out.println("over");
- }
- }
优先级和yield方法
yield()方法称为“退让”,它把运行机会让给了同等级的其他线程.
yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到
可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会.
例子:
- class Demo implements Runnable
- {
- public void run()
- {
- for(int x=0; x<70; x++)
- {
- System.out.println(Thread.currentThread().toString()+"....."+x);
- Thread.yield();//调用Thread的yield方法
- }
- }
- }
- class PriorityYieldDemo
- {
- public static void main(String[] args)throws Exception
- {
- Demo d = new Demo();
- Thread t1 = new Thread(d);
- Thread t2 = new Thread(d);
- t1.start();
- //给t1线程命名为haha
- t1.setName("haha");
- //设置t1线程的优先级为最高级10,最低级为MIN_PRIORITY,中间为NORM_PRIORITY.
- t1.setPriority(Thread.MAX_PRIORITY);
- t2.start();
- for (int x=0; x<80; x++ )
- {
- System.out.println("main....."+x);
- }
- System.out.println("over");
- }
- }
java之yield(),sleep(),join()区别详解
1、sleep()
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常。
例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。
总之,sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。
!!Thread.sleep(long millis)必须带有一个时间参数。
2、join()
join()方法使调用该方法的线程在此之前执行完毕,也就是等待该方法的线程执行完毕后再往下继续执行。注意该方法也需要捕捉异常。
3、yield()
该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会。
yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。
yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会。
!!yield()没有参数.
多线程--线程的匿名内部类写法
- class ThreadTest
- {
- public static void main(String[] args)
- {
- new Thread()
- {
- public void run()
- {
- for (int x=0; x<30; x++)
- {
- System.out.println(Thread.currentThread().toString()+"..."+x);
- }
- }
- }.start();
- new Thread(new Runnable()
- {
- public void run()
- {
- for (int x=0; x<30; x++)
- {
- System.out.println(Thread.currentThread().toString()+"..."+x);
- }
- }
- }).start();
- for (int x=0; x<30; x++)
- {
- System.out.println(Thread.currentThread().toString()+"..."+x);
- }
- }
- }