Java 多线程

Thread

sample

 class MyThread extends Thread {
    
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        //.....
    }
 }

 class Main {
    public static void main(String[] args) {
        MyThread a = new MyThread('A');
        MyThread b = new MyThread('B');
        a.start();
        b.start();
    }
 }

start 方法不能重复调用, 重复调用会出现java.lang.IllegalThreadStateException异常

Runnable

sample

 class MyRunnable extend Runnable{
    public MyRunnable() {

    }

    @Override
    public void run() {

    }
 }

 class Main {
    public static void main(String[] args) {
        MyRunnable a = new MyRunnable('A');
        MyRunnable b = new MyRunnable('B');

        Thread demoa = new Thread(a);
        Thread demob = new Thread(b);

        demoa.start();
        demob.start();
    }
 }

关于选择继承Thread还是实现Runnable接口?

其实Thread也是实现Runnable接口的:

 class Thread implements Runnable {
    //…
    public void run() {
        if (target != null) {
             target.run();
        }
    }
 }

Thread中的run方法调用的是Runnable接口的run方法。Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。

Thread和Runnable的区别

如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

class MyThread implements Runnable{
 
    private int ticket = 5;  //5张票
 
    public void run() {
        for (int i=0; i<=20; i++) {
            if (this.ticket > 0) {
                System.out.println(Thread.currentThread().getName()+ "正在卖票"+this.ticket--);
            }
        }
    }
}
public class lzwCode {
     
    public static void main(String [] args) {
        MyThread my = new MyThread();
        new Thread(my, "1号窗口").start();
        new Thread(my, "2号窗口").start();
        new Thread(my, "3号窗口").start();
    }
}

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。

主线程和子线程之间的关系

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM就是在操作系统中启动了一个进程。

主线程也有可能在子线程结束之前结束。并且子线程不受影响,不会因为主线程的结束而结束。

    // 休眠两秒
    Thread.sleep(2000);

    // 强制加入
    thread.join();

    // 后台线程
    thread.setDaemon(true);

    /**
     * @author Rollen-Holt 后台线程
     * */
    class hello implements Runnable {
        // 虽然有一个死循环,但是程序还是可以执行完的。因为在死循环中的线程操作已经设置为后台运行了。
        public void run() {
            while (true) {
                System.out.println(Thread.currentThread().getName() + "在运行");
            }
        }
     
        public static void main(String[] args) {
            hello he = new hello();
            Thread demo = new Thread(he, "线程");
            demo.setDaemon(true);
            demo.start();
        }
    }


    // 优先级
    // 不要误以为优先级越高就先执行。谁先执行还是取决于谁先去的CPU的资源。
    thread.setPriority(8);

    // 礼让
    // 在线程操作中,也可以使用yield()方法,将一个线程的操作暂时交给其他线程执行。
    Thread.currentThread().yield();

同步和死锁

/**
 * @author Rollen-Holt
 * */
class hello implements Runnable {
    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) {
        hello he=new hello();
        Thread h1=new Thread(he);
        Thread h2=new Thread(he);
        Thread h3=new Thread(he);
        h1.start();
        h2.start();
        h3.start();
    }
    private int count=5;
}

 //【运行结果】:
 // 5
 
 // 4

 // 3

 // 2

 // 1

 // 0

 // -1

这里出现了-1,显然这个是错的。应该票数不能为负值。
如果想解决这种问题,就需要使用同步。所谓同步就是在统一时间段中只有有一个线程运行,其他的线程必须等到这个线程结束之后才能继续执行。

使用线程同步解决问题

使用同步代码块同步方法

同步代码块

    synchronized(同步对象){

        //需要同步的代码

    }
/**
 * @author Rollen-Holt
 * */
class hello implements Runnable {
    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) {
        hello he=new hello();
        Thread h1=new Thread(he);
        Thread h2=new Thread(he);
        Thread h3=new Thread(he);
        h1.start();
        h2.start();
        h3.start();
    }
    private int count=5;
}

同步方法

 synchronized 方法返回类型方法名(参数列表){

    // 其他代码

 }
/**
 * @author Rollen-Holt
 * */
class hello implements Runnable {
    public void run() {
        for (int i = 0; i < 10; ++i) {
            sale();
        }
    }
 
    public synchronized void sale() {
        if (count > 0) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(count--);
        }
    }
 
    public static void main(String[] args) {
        hello he = new hello();
        Thread h1 = new Thread(he);
        Thread h2 = new Thread(he);
        Thread h3 = new Thread(he);
        h1.start();
        h2.start();
        h3.start();
    }
 
    private int count = 5;
}

死锁

当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能导致死锁。

生产者,消费者问题


/*
 *面包类,用于存放厨师生产的面包
 */
public class Bread {
    private String producer;

    public Bread(String producer) {
        super();
        this.producer = producer;
    }

    @Override
    public String toString() {
        return producer;
    }
}


 /* 
 * 篮子类,用于存放面包 
 * 篮子假定最多放10个面包 
 */  
public class Basket {  
    private int index = 0;  
    private Bread[] arrayBread = new Bread[10];  
  
    /* 
     * 此方法用于往篮子里扔面包 每当厨师生成好一个面包就往篮子里边扔 由于当某一个厨师在往篮子扔面包的过程(还没扔完,但是面包已经在篮子里), 
     * 又有一个厨师要往篮子里扔面包。 如果这是篮子里已经有9个面包的话,最后一个厨师就不能在扔了。 
     * 所以需要给这个方法加把锁,等一个厨师扔完后,另外一个厨师才能往篮子里扔。 
     */  
    public synchronized void push(int id, Bread bread) {  
        System.out.println("生成前篮子里有面包:" + index + " 个");  
        // 当厨师发现篮子满了,就在那里不停的等着  
        while (index == arrayBread.length) {  
            System.out.println("篮子满了,我开始等等。。。。。。");  
            try {  
                // 厨师(一个生产线程)开始不停等待  
                // 他需要等待顾客(一个消费线程)把它叫醒  
                this.wait();  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
  
        // 唤醒一个正在等待的线程,如果唤醒的线程为生产线程,则又会进入等待状态,  
        // 如果为消费线程,则因生产线程生产了面包的缘故,消费线程可以进行消费  
        this.notify();  
        arrayBread[index] = bread;  
        index++;  
        System.out.println(bread);  
  
    }  
  
    /* 
     * 此方法用于往篮子里拿面包 加锁原因和上边一样 
     */  
    public synchronized Bread pop(int id) {  
        System.out.println("消费前篮子里有面包:" + index + " 个");  
        while (index == 0) {  
            System.out.println("篮子空了,我开始等等。。。。。。");  
            try {  
                // 顾客(一个消费线程)开始不停等待  
                // 他需要等待厨师(一个生产线程)把它叫醒  
                this.wait();  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
  
        // 唤醒一个正在等待的线程,如果唤醒的线程为消费线程,则又会进入等待状态,  
        // 如果为生产线程,则因生产线程消费了面包的缘故,生产线程可以进行生产  
        this.notify();  
        index--;  
        System.out.println("第" + id + "个顾客消费了 -->" + arrayBread[index]);  
        return arrayBread[index];  
    }  
}

/* 
 * 厨师类,用于生产面包 
 */  
public class Kitchener implements Runnable {  
    private Basket basket;  
    private int id;  
  
    public Kitchener(int id,Basket basket) {  
        super();  
        this.id = id;  
        this.basket = basket;  
    }  
  
    @Override  
    public void run() {  
        //让厨师生产10个面包  
        for (int i = 1; i <= 10; i++) {  
            Bread bread = new Bread("第" + id + "个厨师生成的面包");  
            basket.push(id,bread);  
              
              
            try {  
                Thread.sleep(1);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
  
    }  
  
}  

/* 
 * 顾客类,用于消费面包 
 */  
public class Customer implements Runnable {  
    private Basket basket;  
    private int id;  
    public Customer(int id,Basket basket) {  
        super();  
        this.id = id;  
        this.basket = basket;  
    }  
  
    @Override  
    public void run() {  
        // 让顾客消费10个面包  
        for (int i = 1; i <= 10; i++) {  
            basket.pop(id);  
            try {  
                Thread.sleep(2000);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
  
    }  
  
}

public class Test {  
    public static void main(String[] args) {  
        Basket basket = new Basket();  
        // 两个厨师两个客户  
        Kitchener kitchener1 = new Kitchener(1,basket);  
        Kitchener kitchener2 = new Kitchener(2,basket);  
        Customer customer1 = new Customer(1,basket);  
        Customer customer2 = new Customer(2,basket);  
        new Thread(kitchener1).start();  
        new Thread(kitchener2).start();  
        new Thread(customer1).start();  
        new Thread(customer2).start();  
    }  
  
} 

名字和年龄对应错误了,

http://www.cnblogs.com/rollenholt/archive/2011/08/28/2156357.html


陈学家_6174
3.8k 声望1.1k 粉丝

[链接]