-------android培训、java培训、期待与您交流! ----------
线程和进程:
进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。比如在Windows系统中,一个运行的xx.exe就是一个进程。
线程是指进程中的一个执行任务(控制单元),一个进程中可以运行多个线程,多个线程可共享数据。
注意:一个进程至少有一个线程,为了提高效率,可以在一个进程中开启多个控制单元(也就是多线程)。
进程与线程的区别:
1.进程有独立的进程空间,进程中的数据存放空间(堆空间和栈空间)是独立的。
2.线程的堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可以影响的。
多进程:操作系统中同时运行的多个程序。
多线程:在同一个进程中同时运行的多个任务。
创建线程的方式:
方式一,继承Thread类
1,子类覆写父类中的run方法,将线程运行的代码存放在run中。
2,建立子类对象的同时线程也被创建。
3,通过调用start方法开启线程。
被创建--start()-->运行--sleep()/wait()-->冻结---->消亡
被创建--stop()-->消亡 冻结--notify()/notify()-->运行
运行-->run()/stop()-->消亡
sleep方法需要指定睡眠时间,单位是毫秒。
方式二,实现Runnable接口
1,子类覆盖接口中的run方法。
2,通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。
3,Thread类对象调用start方法开启线程。
为什么要覆盖run方法呢?
Thread类用于描述线程,该类就定义了一个功能用于存储线程要运行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法用于存储线程要运行的代码。
run()和start()的区别:
run()里面封装的是线程执行的代码,如果直接调用,是普通方法调用。
start()做了两件事情:启动了线程,调用了run()方法。
范例:火车站售票
方式一,
class Ticket extends Thread
{
private int tick=100
public void run()
{
while(true)
{
if (tick>0)
{
System.out.println(Thread.currentThread().getName()+"...sale..."+tick--);
}
}
}
}
方式二,
class Ticket implements Runnable
{
private int tick=100;
public void run()
{
while(true)
{
if (tick>0)
{
System.out.println(Thread.currentThread().getName()+"...sale..."+tick--);
}
}
}
}
继承Thread类:
1,不能再继承其他类了因为java只支持单继承。
2,同份资源不共享,比如排队买票,如果用继承Thread类的话只能用一个窗口售票。
实现Runnable方法:
1,多个线程共享一个目标资源,适合多线程处理同一份资源。
2,该类还可以继承其他类,也可以实现其他接口。
线程的安全问题:
导致安全问题的出现的原因:多个线程访问出现延迟以及线程随机性。
注意:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
我们可以通过Thread.sleep(long time)方法来简单模拟延迟情况。
而解决安全问题就需要用到同步代码块或者同步函数。
同步代码块:
格式:
synchronized(对象)
{
需要同步的代码;
}
静态函数的同步:静态函数的同步锁是当前方法所在类的 .class 对象
也就是:synchronized(类名.class)
{
需要同步的代码;
}
同步的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
3,必须保证同步中有一个线程在运行。
同步的好处:解决了多线程的安全问题。
同步的弊端:多个线程都需要判断锁,较为消耗资源。
/*
* 四个窗口买一百张票
*/
class Ticket implements Runnable{
private int num = 100 ;
public void run(){
while(true){
synchronized(Ticket.class)//因为类的字节码是唯一的,所以可以用它作为锁
{
if(num>0)
{
try{Thread.sleep(10);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".....sale....."+num--);
}
}
}
}
}
public class TicketTest {
public static void main(String[] args) {
Ticket t = new Ticket();
for(int x=0;x<4;x++){
new Thread(t).start();
}
}
}
单例设计模式:
解决问题:保证了一个类在内存中只能有一个对象,也就是保证了对象的唯一性。
创建单例的步骤:
1,将该类中的构造函数私有化。
2,在本类中创建一个本类对象。
3,定义一个方法,返回值类型是本类类型。让其他程序通过该方法就可以获取到该类对象。
范例:单例模式-懒汉式,因为getInstance方法中有多条语句,当程序执行到该方法时有可能出现第一条语句还没执行完第二条语句就开始执行了,这样就造成了线程的安全:
class Worker{
//懒汉式
private static Worker worker = null ;
private Worker(){}
private Worker(String name, int age) {//将构造函数私有化
super();
this.name = name;
this.age = age;
}
public static Worker getInstance(){//定义一个方法,返回值类型是本类类型。让其他程序通过该方法就可以获取到该类对象。
if(worker==null){
synchronized (Worker.class) {
if(worker==null)
worker = new Worker("lisi",30);
}
}
return worker;
}
private String name ;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
如果使用饿汗式的话就可以避免线程安全的问题:例如Worker类的单例:
class Worker{
//饿汉式
private static Worker worker = new Worker("lisi",30);//在本类中创建本类对象
private Worker(String name, int age) {//将构造函数私有化
super();
this.name = name;
this.age = age;
}
public static Worker getInstance(){//定义一个方法,返回值类型是本类类型。让其他程序通过该方法就可以获取到该类对象。
return worker;
}
private String name ;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
同步函数:在函数上加上synchronized修饰符即可。
主要以:同步函数的锁是this
死锁:
当两个线程相互等着对方释放锁。
等着要对方的结果才能继续执行就会发生死锁。
在开发中要避免死锁的发生。
线程间的通讯:
线程间通讯:其实就是多个线程在操作同一个资源,但是操作的动作不同。
wait; notify(); notifyAll;
都使用在同步中,因为要对持有监视器(锁)的线程进行操作,所以要使用在同步中,因为只有同步才具有锁。
wait():让当前线程放弃监视器进入等待,直到其他线程调用同一个监视器并调用notify()或notifyAll()为止。
notify():唤醒在同一对象监听器中调用wait方法的第一个线程。
notifyAll():唤醒在同一对象监听器中调用wait方法的所有线程。
wait(),sleep()有什么区别?
wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,不释放锁。
为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步线程时,都必须要标识它们所操作线程持有的锁,只有同一个锁上的被等待线程可以被同一个锁notify唤醒。不可以对不同锁的线程进行唤醒,也就是说等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。
class Resource{
private String name;
private int count=1;
private boolean flag=false;
public synchronized void set(String name)
{
while(flag) //在此不可以用if,因为if里语句只循环一次,而whlie循环多次
try{this.wait();}catch(Exception e){}
this.name=name+"----"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;
this.notifyAll();
}
public synchronized void out()
{
while(!flag)
try{this.wait();}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"...消费者......"+this.name);
flag=false;
this.notifyAll();
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
res.set("++商品++");
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
public class ProducersConsumersDemo {
public static void main(String[] args) {
Resource r=new Resource();
Producer pro=new Producer(r);
Consumer con=new Consumer(r);
new Thread(pro).start();
new Thread(pro).start();
new Thread(con).start();
new Thread(con).start();
}
}
JDK1.5中提供了多线程升级解决方案,将同步synchronized替换成现实Lock操作。
将Object中的wait,notify,notifyAll,替换了Condition对象,该对象可以Lock进行获取。
condition中的方法: await(): 等价于同步监听器的wait()方法;
signal(): 等价于同步监听器的notify()方法;
signalAll(): 等价于同步监听器的notifyAll()方法;
Thread类中停止线程的方法 interrupt();
join:
当A线程执行到B线程的.join方法时,A就会等待,等B线程都执行完A才会执行。join可用来临时加入线程执行。