---------------------- android培训、java培训、期待与您交流! ----------------------
在java中要想实现多线程,有两种手段,一种是继续Thread类,另外一种是实现Runable接口。
对于直接继承Thread的类来说,代码大致框架是:
class 类名 extends Thread
{
属性1;
属性2;
...
方法1;
方法2;
...
public void run()
{
// other code
}
}
一个简单的示例:
class Hello extends Thread
{
private String name ;
public Hello()
{
}
public Hello(String name)
{
this.name = name;
}
public void run()
{
for(int i=1; i<30; i++)
System.out.println(name + "运行 "+ i);
}
}
class ThreadTest
{
public static void main(String[] args){
Hello h1 = new Hello("A");
Hello h2 = new Hello("B");
h1.start();
h2.start();
}
}
因为需要用到CPU的资源,所以每次的运行结果基本是都不一样的,呵呵。
注意:虽然我们在这里启动线程调用的是start()方法,但是实际上调用的还是run()方法的主体。
通过实现Runnable接口:
大致框架是:
class 类名 implements Runnable
{
属性1 ;
属性2 ;
...
方法1 ;
方法2 ;
...
public void run()
{
// other code ;
}
}
一个简单示例如下:
/**
* @author 冯兵兵 实现Runnable接口
*
* */
class Hello implements Runnable
{
private String name ;
public Hello()
{
}
public Hello(String name)
{
this.name = name;
}
public void run()
{
for(int i=1; i<10; i++)
System.out.println(name + "运行 "+ i);
}
}
class ThreadTest
{
public static void main(String[] args){
Hello h1 = new Hello("线程A");
Hello h2 = new Hello("线程B");
Thread t1 = new Thread(h1);
Thread t2 = new Thread(h2);
t1.start();
t2.start();
}
}
【可能的运行结果】:
关于选择继承Thread还是实现Runnable接口?
其实Thread也是实现Runnable接口的:
public
class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
其实Thread中的run方法调用的是Runnable接口的run方法。不知道大家发现没有,Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性。
在定义线程时,建立使用实现方式。
两种方式区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存在接口的子类的run方法。利于资源的共享。
例如模拟一个买票程序:
/**
* @author 冯兵兵 模拟售票系统
*
* */
class Ticket implements Runnable
{
private int tick = 20;
public void run()
{
while(true)
{
if(tick>0)
{
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);//创建了一个线程;
t1.start();
t2.start();
t3.start();
}
}
【可能的运行结果】:
总结一下吧:
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
线程的休眠:
/**
* @author 冯兵兵 线程的休眠
*
* */
class Hello implements Runnable
{
public void run()
{
for(int i=1; i<5; i++)
{
try{
Thread.sleep(1000);
}
catch(Exception e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+i);
}
}
}
class ThreadTest
{
public static void main(String[] args){
Hello h1 = new Hello();
Thread t1 = new Thread(h1,"线程A");
t1.start();
}
}
运行结果:每隔1秒输出一个
线程A1
线程A2
线程A3
线程A4
线程的优先级:
/**
* @author 冯兵兵 线程的优先级
*
* */
class Hello implements Runnable
{
public void run()
{
for(int i=1; i<5; i++)
{
System.out.println(Thread.currentThread().getName()+"运行"+i);
}
}
}
class ThreadTest
{
public static void main(String[] args){
Thread t1 = new Thread(new Hello(),"线程A");
Thread t2 = new Thread(new Hello(),"线程B");
Thread t3 = new Thread(new Hello(),"线程C");
t1.setPriority(8); // 设置t1的优先级为8
t2.setPriority(2); // 设置t2的优先级为2
t3.setPriority(5); // 设置t3的优先级为5
t1.start();
t2.start();
t3.start();
}
}
可能运行结果:
注意:不要误以为优先级越高就先执行。谁先执行还是取决于谁先获得CPU的资源。
线程同步与死锁
class Sale implements Runnable {
private int count=5;
public void run() {
for(int i=0;i<10;++i){
if(count>0){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(count--);
}
}
}
public static void main(String[] args) {
Sale s=new Sale();
Thread h1=new Thread(s);
Thread h2=new Thread(s);
Thread h3=new Thread(s);
h1.start();
h2.start();
h3.start();
}
}
运行结果:
5
4
3
2
1
0
-1
这里出现了-1,显然这个是错的。,应该票数不能为负值。
如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,
其他的线程必须等到这个线程结束之后才能继续执行。
【使用线程同步解决问题】
采用同步的话,可以使用同步代码块和同步方法两种来完成。
【同步代码块】:
语法格式:
synchronized(同步对象){
//需要同步的代码
}
但是一般都把当前对象this作为同步对象。
class Sale implements Runnable {
private int count=5;
public void run() {
for(int i=0;i<10;++i){
synchronized(this){
if(count>0){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(count--);
}
}
}
}
public static void main(String[] args) {
Sale s=new Sale();
Thread h1=new Thread(s);
Thread h2=new Thread(s);
Thread h3=new Thread(s);
h1.start();
h2.start();
h3.start();
}
}
【运行结果】:(每一秒输出一个结果)
5
4
3
2
1
【同步方法】
也可以采用同步方法。
语法格式为synchronized 方法返回类型 方法名(参数列表){
// 其他代码
}
这里不再赘述。
下面介绍JDK1.5 中提供了多线程升级解决方案。
将同步Synchronized替换成显式Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。
该对象可以Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock
unlock
newCondition()
Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。
此处列举经典的生产者和消费者问题。
【生产者和消费者问题】
先使用传统的线程技术解决生产者消费问题,代码如下:
class ProducerConsumerDemo
{
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;
// t1 t2
public synchronized void set(String name)
{
while(flag)
try{this.wait();}catch(Exception e){}//t1(放弃资格) t2(获取资格)
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
this.notifyAll();
}
// t3 t4
public synchronized void out()
{
while(!flag)
try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
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();
}
}
}
运行结果:
生产者生成了一个商品,消费者才能消费一个商品,没有问题。
下面我们用JDK1.5新特性来解决这个问题
import java.util.concurrent.locks.*;
class ProducerConsumerDemo2
{
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;
// t1 t2
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();//t1,t2
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产
者.."+this.name);
flag = true;
condition_con.signal();
}
finally
{
lock.unlock();//释放锁的动作一定要执行。
}
}
// t3 t4
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
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("+商品+");
}
catch (InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}
Lock
实现提供了比使用 synchronized
方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition
对象。 但是,当锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁。
---------------------- android培训、java培训、期待与您交流! ----------------------