多线程 生产者&消费者 哲学家进餐 & random & synchronized & Thread & Sleep

基础知识点

  • sleep()不会放开锁,而wait()会放开锁,让其他的进程执行

两种实现方法

 public class TestThread1 {
    public static void main(String args[]) {
        Runner1 r = new Runner1();
        r.start();
        //r.run();
        //Thread t = new Thread(r);
        //t.start();
        for(int i=0; i<100; i++) {
            System.out.println("Main Thread:------" + i);
        }
    }
}
//class Runner1 implements Runnable {
class Runner1 extends Thread {
    public void run() {
        for(int i=0; i<100; i++) {
            System.out.println("Runner1 :" + i);
        }
    }
}

测试 yield

  • 短暂的sleep,给其他进程让出一点时间
public class TestYield {
    public static void main(String[] args) {
        MyThread3 t1 = new MyThread3("t1");
        MyThread3 t2 = new MyThread3("t2");
        t1.start();
        t2.start();
    }
}
class MyThread3 extends Thread {
    MyThread3(String s) {
        super(s);
    }
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(getName() + ": " + i);
            if (i % 10 == 0) {
                yield();
            }
        }
    }
}

测试 synchronized

  • 观察b的输出结果,看synchronized 是否影响b 的访问
  • 一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
  • 二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
  • 三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
  • 四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞.
  • 五、以上规则对其它对象锁同样适用.
public class TT implements Runnable {
    int b = 100;
    public synchronized void m1() throws Exception{
        //Thread.sleep(2000);
        b = 1000;
        Thread.sleep(5000);
        System.out.println("b = " + b);
    }
//  public synchronized void m2() throws Exception {
    public void m2() throws Exception {
        Thread.sleep(2500);
        b = 2000;
    }
    public void run() {
        try {
            m1();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws Exception {
        TT tt = new TT();
        Thread t = new Thread(tt);
        t.start();
        tt.m2();
        System.out.println(tt.b);
    }
}

java 中 volatile 关键字

  • volatile的主要作用是:提示编译器该对象的值有可能在编译器未监测的情况下被改变。
  • 如果一个基本变量被volatile修饰,编译器将不会把它保存到寄存器中,而是每一次都去访问内存中实际保存该变量的位置上。这一点就避免了没有volatile修饰的变量在多线程的读写中所产生的由于编译器优化所导致的灾难性问题。所以多线程中必须要共享的基本变量一定要加上volatile修饰符。当然了,volatile还能让你在编译时期捕捉到非线程安全的代码。
  • Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式。
  • 下面例子运行结果还是没有我们期望的1000,原因见
  • http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
public class Counter {
    public volatile static int count = 0;
    public static void inc() {
        //这里延迟1毫秒,使得结果明显
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
        }
        count++;
    }
    public static void main(String[] args) {
        //同时启动1000个线程,去进行i++计算,看看实际结果
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Counter.inc();
                }
            }).start();
        }    
        //这里每次运行的值都有可能不同,可能为1000
        System.out.println("运行结果:Counter.count=" + Counter.count);
    }
}

经典死锁例子

public class TestDeadLock implements Runnable {
    public int flag = 1;
    static Object o1 = new Object(), o2 = new Object();
    public void run() {
System.out.println("flag=" + flag);
        if (flag == 1) {
            synchronized (o1) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    System.out.println("1");
                }
            }
        }
        if (flag == 0) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    System.out.println("0");
                }
            }
        }
    }
    public static void main(String[] args) {
        TestDeadLock td1 = new TestDeadLock();
        TestDeadLock td2 = new TestDeadLock();
        td1.flag = 1;
        td2.flag = 0;
        Thread t1 = new Thread(td1);
        Thread t2 = new Thread(td2);
        t1.start();
        t2.start();
    }
}

生产者和消费者

涉及到的小知识点

  • 利用URL加载图片
  • 解决第一次图片加载不成功
  • 同一类中的方法加synchronized 锁

ProducerConsumer.java

  • 类的粒度太小一个java文件就够了
  • 可以直接运行的
  • 提供了图形界面和非图形界面两种版本
  • 参考soldier线程中的例子
/*  范例名称:生产者--消费者问题
 *  源文件名称:ProducerConsumer.java
 *  要  点:
 *      1. 共享数据的不一致性/临界资源的保护
 *      2. Java对象锁的概念
 *      3. synchronized关键字/wait()及notify()方法
 */
public class ProducerConsumer {
    public static void main(String[] args) {
        SyncStack ss = new SyncStack();
        Producer p = new Producer(ss);
        Consumer c = new Consumer(ss);
        new Thread(p).start();
//        new Thread(p).start();
//        new Thread(p).start();
        new Thread(c).start();
    }
}
class WoTou {
    int id; 
    WoTou(int id) {
        this.id = id;
    }
    public String toString() {
        return "WoTou : " + id;
    }
}
class SyncStack {
    int index = 0;
    WoTou[] arrWT = new WoTou[3];
    public synchronized void push(WoTou wt) {
        while(index == arrWT.length) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notifyAll();       
        arrWT[index] = wt;
        index ++;
    }
    public synchronized WoTou pop() {
        while(index == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notifyAll();
        index--;
        return arrWT[index];
    }
}
class Producer implements Runnable {
    public static int len1 = 0;//留给图形化界面用
    SyncStack ss = null;
    Producer(SyncStack ss) {
        this.ss = ss;
    }
    public void run() {
        for(int i=0; i<100; i++) {
            WoTou wt = new WoTou(i);
            ss.push(wt);


            len1 += 1;
            if(len1 > 100) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
System.out.println("len1 = " + len1);          
System.out.println("生产了: " + wt);
            try {
                Thread.sleep((int)(Math.random() * 200));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }           
        }
    }
}
class Consumer implements Runnable {
    public static int len2 = 0;//留给图形化界面用
    SyncStack ss = null;
    Consumer(SyncStack ss) {
        this.ss = ss;
    }
    public void run() {
        for(int i=0; i<100; i++) {
            WoTou wt = ss.pop();
            len2 += 1;
            if(len2 > 100) {
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
System.out.println("len2 = " + len2);             
System.out.println("消费了: " + wt);
            try {
                Thread.sleep((int)(Math.random() * 1000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }           
        }
    }
}

MainFrame.java

  • 在src的同级目录下建立images文件夹
  • 命名图片0.jpg, 1.jpg
  • 这里会解决一下坦克第一次爆照不出图片的问题
  • 使用的是URL定位图片资源的
  • 出自操作系统上机实验的作业
  • 参考soldier坦克大战图片版
  • 是上面的ProducerConsumer.java的图片版,要放在一起使用,使用时注释掉ProducerConsumer.java中那几个syso输出语句
  • ProducerConsumer.java也可以单独使用
import java.awt.*;
import java.awt.event.*;
public class MainFrame extends Frame{
    private static Toolkit tk = Toolkit.getDefaultToolkit();
    private static boolean init = false;
    SyncStack ss;
    Producer p;
    Consumer c;
    private static Image[] imgs = {
            tk.getImage(MainFrame.class.getClassLoader().getResource("images/0.jpg")),
            tk.getImage(MainFrame.class.getClassLoader().getResource("images/1.jpg")),
    };
    public static final int REC_WIDTH = 600;
    public static final int REC_HEIGH = 15;
    public static final int REC1_X = 100;
    public static final int REC1_Y = 300;
    public static final int REC2_X = 100;
    public static final int REC2_Y = 500;
    BloodBar bar1 = new BloodBar(REC1_X, REC1_Y, REC_WIDTH, REC_HEIGH, 0);
    BloodBar bar2 = new BloodBar(REC2_X, REC2_Y, REC_WIDTH, REC_HEIGH, 1);
    public void paint(Graphics g) {
        bar1.draw(g);
        bar2.draw(g);
    }
    public void launchFrame() {
        setLocation(100, 100);
        setSize(800, 600);
        setResizable(false);
        setBackground(Color.green);
        setTitle("Police_Thief");
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        addKeyListener(new KeyMonitor());
        setVisible(true);
        new Thread(new PaintThread()).start();
    }
    public static void main(String[] args) {
        new MainFrame().launchFrame();
    }
    private class KeyMonitor extends KeyAdapter {
        @Override
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            if(key == KeyEvent.VK_CONTROL) {
                ss = new SyncStack();
                p = new Producer(ss);
                c = new Consumer(ss);
                new Thread(p).start();
                new Thread(c).start();
            }
        }

    }
    private class PaintThread implements Runnable {
        public void run() {
            while(true) {//这里注意一下
                repaint();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    private class BloodBar {//画警察和小偷血条的类
        int x, y, width, heigh, id;
        public BloodBar(int x, int y, int width, int heigh, int id) {
            this.x = x; 
            this.y = y; 
            this.width = width; 
            this.heigh = heigh;
            this.id = id;
        }
        public void draw(Graphics g) {
            if(!init) {//这样做是为了解决2.8_2第一次花爆炸画不出来
                for (int i = 0; i < imgs.length; i++) {
                    g.drawImage(imgs[i], -100, -100, null);
                }           
                init = true;
            }
            Color color = g.getColor();
            g.setColor(Color.RED);
            g.drawRect(x, y, width, heigh);
            int w;
            if(id == 0)
                w = p.len1;
            else
                w = c.len2;
            int ww = w * (REC_WIDTH / 100);//将血条的长度设置成100
            g.fillRect(x, y, ww, heigh);
            g.setColor(color);
            g.drawImage(imgs[id], x + ww, y - 100, null);
            g.drawString(w + "", x + ww, y + 30);
        }
    }
}

哲学家进餐

涉及到的小知识点

  • 进程等待任意时间
  • 建立ChopstickArray来管理Chopsticks

DiningPhilosophersFrame.java

  • 操作系统上机实验作业
  • 这是swing版本的
  • Frame版本的在我的网盘里,由于代码大部分类似,没有贴出来
public class DiningPhilosophersFrame extends JFrame {
    private final JPanel panel1 = new JPanel();
    private final JPanel panel2 = new JPanel();
    private final JTextArea thinkingTextArea = new JTextArea(5, 10);
    private final JTextArea eatingTextArea = new JTextArea(5, 10);
    private final JTextArea waitingTextArea = new JTextArea(5, 10);
    JLabel label1 = new JLabel("哲学家问题");
    JLabel label2 = new JLabel("思考");
    JLabel label3 = new JLabel("吃饭");
    JLabel label4 = new JLabel("等待");
    JButton button = new JButton("开始运行");
    public DiningPhilosophersFrame() {
        panel2.setLayout(new GridLayout(2, 2, 3, 3));
        panel2.add(label2);
        panel2.add(label3);
        panel2.add(label4);
        JScrollPane js1 = new JScrollPane(thinkingTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        JScrollPane js2 = new JScrollPane(eatingTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        JScrollPane js3 = new JScrollPane(waitingTextArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        panel2.add(js1);
        panel2.add(js2);
        panel2.add(js3);
        panel1.setLayout(new FlowLayout());
        panel1.add(label1);
        panel1.add(panel2);
        panel1.add(button);
        setContentPane(panel1);
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                ChopstickArray chopstickArray = new ChopstickArray(5);
                for (int i = 0; i < 5; i++) {
                    new Thread(new Philosopher(i, chopstickArray, thinkingTextArea, eatingTextArea, waitingTextArea))
                            .start();
                }
            }
        });
        setSize(300, 400);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    public static void main(String[] args) {
        new DiningPhilosophersFrame();
    }
}

Philosopher.java

public class Philosopher implements Runnable {
    private int id;
    private boolean state; //false停止思考,寻找筷子  true在思考
    ChopstickArray chopstickArray;
    JTextArea thinkingTextArea;
    JTextArea eatingTextArea;
    JTextArea waitingTextArea;
    public Philosopher(int id, ChopstickArray chopstickArray, JTextArea thinkingTextArea, JTextArea eatingtextArea,
            JTextArea waitingTextArea) {
        this.id = id;
        this.chopstickArray = chopstickArray;
        this.thinkingTextArea = thinkingTextArea;
        this.eatingTextArea = eatingtextArea;
        this.waitingTextArea = waitingTextArea;
    }
    public synchronized void thinking() {
        if (state) { // 如果在思考,说明这个哲学家两面的筷子没用
            chopstickArray.getById(id).setAvailable(true);
            chopstickArray.getLast(id).setAvailable(true);
            String text = thinkingTextArea.getText();
            thinkingTextArea.setText(text + this + "在思考\n");
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        state = false;//开始找筷子
    }
    public synchronized void eating() {
        if (!state) { // 开始找筷子
            if (chopstickArray.getById(id).isAvailable()) { // 如果哲学家右手边的筷子可用
                if (chopstickArray.getLast(id).isAvailable()) { // 如果左手边的筷子也可用
                    // 然后将这个能吃饭的哲学家两侧的筷子都设置为不可用
                    chopstickArray.getById(id).setAvailable(false);
                    chopstickArray.getLast(id).setAvailable(false);
                    String text = eatingTextArea.getText();
                    eatingTextArea.setText(text + this + "在吃饭\n");
                    try {
                        Thread.sleep(1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else {
                    // 右手边的筷子可用,但是左手边的不可用
                    String str = waitingTextArea.getText();
                    waitingTextArea.setText(str + this + "在等待" + chopstickArray.getLast(id) + "\n");
                    try {
                        wait(new Random().nextInt(100));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } else {
                // 如果哲学家右手边的筷子不可用则等待
                String str = waitingTextArea.getText();
                waitingTextArea.setText(str + this + "在等待" + chopstickArray.getById(id) + "\n");
                try {
                    wait(new Random().nextInt(100));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        state = true;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; ++i) {
            thinking();
            eating();
        }
    }
    @Override
    public String toString() {
        return " 哲学家 " + id;
    }
}

ChopstickArray.java

public class ChopstickArray {
    private Chopstick[] chopsticks;
    public ChopstickArray(int size) {
        chopsticks = new Chopstick[size];
        for (int i = 0; i < chopsticks.length; ++i) {
            chopsticks[i] = new Chopstick(i);
        }
    }
    public Chopstick getById(int id) {
        return chopsticks[id];
    }
    public Chopstick getLast(int id) {
        if (id == 0) {
            return chopsticks[chopsticks.length - 1];
        } else {
            return chopsticks[id - 1];
        }
    }
}

Chopstick.java

public class Chopstick {
    private volatile boolean available = true;
    private int id;
    public Chopstick(int id) {
        this.id = id;
    }
    public boolean isAvailable() {
        return available;
    }
    public void setAvailable(boolean available) {
        this.available = available;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Override
    public String toString() {
        return "筷子" + id;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值