------- android培训、java培训、期待与您交流! ----------
多线程
概述
进程:是一个正在执行的程序。每一个进程执行都有一个执行程序,该顺序是一个执行路径,或者叫做控制单元。
线程就是进程中的一个独立控制的单元。一个进程至少有一个线程。
创建线程的第一种方式:继承Thread类。
步骤:
1:定义类继承Thread
2:复写Thread类中的run()方法。
3:调用线程的start()方法。该方法有两个作用,启动线程,调用run方法。
class Test extends Thread//继承Thread类
{
private String name;
Test(String name)
{
this.name=name;
}
public void run()//复写run方法
{
for(int x =0; x<60; x++)
System.out.println(name+"run...."+x);
}
}
class ThreadTest
{
public static void main(String[] args)
{
Test t1 = new Test("one");//创建线程对象
Test t2 = new Test("two");
t1.start();
t2.start();//启动线程
for(int x =0; x<60; x++)
System.out.println("main run...."+x);
}
}
{
private String name;
Test(String name)
{
this.name=name;
}
public void run()//复写run方法
{
for(int x =0; x<60; x++)
System.out.println(name+"run...."+x);
}
}
class ThreadTest
{
public static void main(String[] args)
{
Test t1 = new Test("one");//创建线程对象
Test t2 = new Test("two");
t1.start();
t2.start();//启动线程
for(int x =0; x<60; x++)
System.out.println("main run...."+x);
}
}
多线程具有随机性。
Thread类中的run方法用于存储线程要运行的代码,所以要复写run方法。
d.start();开启线程并执行该线程的run方法。
d.run();仅仅是对象调用方法,而线程创建了,并没有运行
线程都有自己的默认名称:Thread——编号,该编号从零开始。
currentThread();获取当前线程对象。
getName();获取线程名称。
局部变量在每一个线程中都有独立一份。
创建线程的第二种方式:实现Runable接口。
步骤:
1:定义类实现Runable接口。
2:覆盖Runable接口中的run()方法。
3:通过Thread类建立线程对象。
4:将Runable接口的子类对象作为实际参数传递给Thread类的构造函数。
5:调用Thread类中的start()方法开启线程并调用Runable接口子类的run方法。
自定义的run方法所属对象是Runable接口的子类对象。所以要让线程去指定对象的的run方法就必须明确该run方法所属的对象。
实现方式的好处:避免了单继承的局限性,在定义线程时建议使用实现方式。
区别:
继承Thread类:线程代码存放在Thread子类的run方法中。
实现Runable接口:线程代码存放在接口的子类的run方法中。
当多条语句在操作同一个线程共享的数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程就参与进来执行,
导致共享数据错误。
解决办法:
对多条操作共享数据的语句,只能让一下线程执行完,在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式:同步代码块。
synchronized(对象)
{
需要被同步的代码块
}
对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的的线程及时获取cpu执行权,也进不去,因为没有获取锁。
同步的前提:
1:必须要有两个或两个以上的线程。
2:必须是多个线程使用同一个锁。
3:必须保证同步中只有一个线程在运行。
优点:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
明确哪些代码是多线程代码,明确共享数据,明确多线程代码哪些语句是操作共享数据。
函数需要被对象调用,那么函数都有一个所属对象引用,就是this所以同步函数使用的锁就是this。
class Ticket extends Thread
{
private int ticket = 100;
Object obj = new Object();//锁可以是对象,所以被任意对象调用应定义Object对象
public void run()
{
while(true)
{
synchronized(obj)//同步代码块
{
if(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"..."+ticket--);
}
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();//建立对象
Ticket t3 = new Ticket();
Ticket t4 = new Ticket();
t1.start();
t2.start();
t3.start();//开启并调用线程
t4.start();
}
}
{
private int ticket = 100;
Object obj = new Object();//锁可以是对象,所以被任意对象调用应定义Object对象
public void run()
{
while(true)
{
synchronized(obj)//同步代码块
{
if(ticket>0)
{
System.out.println(Thread.currentThread().getName()+"..."+ticket--);
}
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();//建立对象
Ticket t3 = new Ticket();
Ticket t4 = new Ticket();
t1.start();
t2.start();
t3.start();//开启并调用线程
t4.start();
}
}
静态的同步方法使用的是锁是该方法所在类的字节码文件对象,类名.class。
懒汉式双重判定(节省时间)
class Single
{
private static single = null;
private single(){}
public static single getInstance()
{
if(s==null)
{
synchronized(single.class)
{
if(s==null)
s = new Single();
}
}
return single;
}
}
死锁
同步中嵌套同步,而锁却不同。都想进对方的线程拿锁却不肯放自己的锁,造成程序停止。
多线程间通讯
多个线程在操作同一个资源但是操作的动作不同。
等待唤醒机制;
notify(); 唤醒当前。
notifyAll();唤醒线程池中所有线程。
wait();等待。
都是使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才有锁。
等待和唤醒必须是同一个锁而锁可以是任意对象,所以可以被任意对象调用的方法定义在object类中。
jdk1.5中提供令人多线程升级解决方案。将同步synchronized替换成显示lock操作,将object中的wait,notify,notifyAll替换成了condition对象。
释放锁的动作一定要执行。
import java.util.concurrent.locks.*;
class ProducerConsumerDemo1
{
public static void main(String[] args)
{
Resource r = new Resource();
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 Resource
{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name)throws InterruptedException
{
lock.lock();//生产者线程拿锁
try
{
while(flag)
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()throws InterruptedException
{
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 implements Runnable//实现Runnable接口创建线程
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()//复写run方法
{
try
{
while(true)
res.set("+商品+");
}
catch (InterruptedException e)
{
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
try
{
while(true)
res.out();
}
catch (InterruptedException e)
{
}
}
}
class ProducerConsumerDemo1
{
public static void main(String[] args)
{
Resource r = new Resource();
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 Resource
{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name)throws InterruptedException
{
lock.lock();//生产者线程拿锁
try
{
while(flag)
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()throws InterruptedException
{
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 implements Runnable//实现Runnable接口创建线程
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()//复写run方法
{
try
{
while(true)
res.set("+商品+");
}
catch (InterruptedException e)
{
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
try
{
while(true)
res.out();
}
catch (InterruptedException e)
{
}
}
}
停止线程
当线程处于冻结状态,就不会独到标记那么线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结
进行清除强制让线程恢复到运行状态中来这样就可以操作标记让线程结束。
Thread类提供的方法 interrupt();
set Dameo 守护线程,该方法必须在启动线程前调用,当正在运行的线程都是守护线程时,java虚拟机会退出。