线程的方法、优先级及同步

来源:互联网 发布:寻侠武功生肖突破数据 编辑:程序博客网 时间:2024/06/14 10:24

一、操作线程的方法

1、线程的休眠

sleep()方法需要一个参数用于指定的该线程休眠的时间,该时间以毫秒为单位。sleep()方法通常在run()方法内的循环中被使用。

sleep()方法的语法如下:

        try {            Thread.sleep(2000);            //中断异常        } catch (InterruptedException e) {            e.printStackTrace();        }    }

虽然使用了sleep()方法的线程在一段时间内会醒来,但是并不能保证它醒来后进入运行状态,只能保证它进入就绪状态。

例1、在项目中创建SleepMethodTest类,该类继承了JFrame类,实现在窗体中自动画线段的功能,并且为线段设置颜色,颜色是随机产生。

package com.eistert.process;import java.awt.Color;import java.awt.Graphics;import java.util.Random;import javax.naming.InitialContext;import javax.swing.JFrame;public class SleepMethodTest extends JFrame {    private Thread t;    // 定义颜色数组    private static Color[] color = { Color.BLACK, Color.BLUE, Color.CYAN,            Color.GREEN, Color.ORANGE, Color.YELLOW, Color.RED, Color.PINK,            Color.LIGHT_GRAY };    // 创建随机对象    private static final Random rand = new Random();    // 获取随机颜色值的方法    private static Color getC() {        return color[rand.nextInt(color.length)];    }    public SleepMethodTest() {        t = new Thread(new Runnable() {// 创建匿名线程对象                    int x = 30;// 定义初始坐标                    int y = 50;                    @Override                    public void run() {// 覆盖线程接口方法                        while (true) {                            try {                                Thread.sleep(100);// 线程休息0.1秒                            } catch (InterruptedException e) {                                e.printStackTrace();                            }                            // 获取组件绘图上下文对象                            Graphics graphics = getGraphics();                            // 设置绘图颜色                            graphics.setColor(getC());// 设置绘图颜色                            // 绘制直线并递增垂直坐标                            graphics.drawLine(x, y, 100, y++);                            if (y >= 80) {                                y = 50;                            }                        }                    }                });        t.start();// 启动线程    }    // 初始化程序界面的方法    public static void init(JFrame frame, int width, int height) {        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        frame.setSize(width, height);        frame.setVisible(true);    }    public static void main(String[] args) {        init(new SleepMethodTest(), 100, 100);    }}

在本实例中定义了getC()方法,该方法用于随机产生Color类型的对象,并且在产生线程的匿名内部类中使用getGraphics()方法获取Graphics对象,使用该对象调用setColor()方法为图形设置颜色;调用drawLine()方法绘制一条线段。同时线段会根据纵坐标的变化自动调整。

2、线程的加入

如果当前某程序为多线程程序,假如存在一个线程A,现在需要插入线程B,并要求线程B先执行完毕,然后在继续执行线程A,此时可以使用Thread类中join()方法来完成。这就好比此时读者正在看电视,突然有人上门收水费,读者必须付完水费后才能继续看电视。

当某个线程使用join()方法加入到另外一个线程时,另一个线程会等待该线程执行完毕后再继续执行。

例2、在项目中创建JoinTest类,该类继承了JFrame。该实例包括两个进度条,进度条的进度由线程来控制,通过使用join()方法使上面的进度条必须等待下面的进度条完成之后可以继续。

package com.eistert.process;import java.awt.BorderLayout;import javax.swing.JFrame;import javax.swing.JProgressBar;public class JoinTest extends JFrame {    // 定义两个线程    private Thread threadA;    private Thread threadB;    // 定义两个进度条组件    final JProgressBar progresBar = new JProgressBar();    final JProgressBar progresBar2 = new JProgressBar();    int count = 0;    // 构造方法    public JoinTest() {        super();        // 将进度条设置在窗体的做背面        getContentPane().add(progresBar, BorderLayout.NORTH);        // 将进度条设置在窗体的最南面        getContentPane().add(progresBar2, BorderLayout.SOUTH);        // 设置进度条显示数字字符        progresBar.setStringPainted(true);        progresBar2.setStringPainted(true);        // 使用匿名内部类形式初始化Thread实例        threadA = new Thread(new Runnable() {            int count = 0;            @Override            public void run() {                // 设置进度条的当前值                while (true) {                    progresBar.setValue(++count);                    try {                        // 使线程A休眠100毫秒                        Thread.sleep(100);                        // 使线程B调用join()方法                        threadB.join();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        });        threadA.start();// 启动线程A        threadB = new Thread(new Runnable() {            int count = 0;            @Override            public void run() {                while (true) {                    // 设置进度条的当前值                    progresBar2.setValue(++count);// 设置进度条的当前值                    try {                        Thread.sleep(100);// 使线程B休眠100毫秒                    } catch (InterruptedException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                    if (count == 100) {                        break;                    }                }            }        }        );        threadB.start();// 启动线程B    }    // 设置窗体的各种的属性的方法    public static void init(JFrame frame, int width, int height) {        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        frame.setSize(width, height);        frame.setVisible(true);    }    public static void main(String[] args) {        init(new JoinTest(), 100, 100);    }}

在本实例中同时创建了两个线程,这两个线程分别负责进度条的滚动,在线程A的run()方法中使线程B的 对象调用join()方法,而join()方法使当前运行线程暂停,直到调用join()方法的线程执行完毕后在执行,所以线程A等待线程B执行完毕后再开始执行,及下面的进度条滚动完毕后上面的进度条才开始滚动。

3、线程的中断;

现在提倡在run()方法中使用无限循环的形式,然后使用一个布尔型标记控制循环的停止。

package com.eistert.process;public class InterruptedTest implements Runnable {    // 设置一个标记变量,默认值为false    private boolean isContinue = false;    @Override    public void run() {// 重写run()方法        while (true) {            // ...            if (isContinue) {// 当isContinue变量为true时,停止线程                break;            }        }    }    public void setContinue() {// 定义设置isContinue变量为True的方法        this.isContinue = true;    }}

例3,在项目中创建InterruptedSwing类,该类实现了Runnable接口,创建一个精度条,在表示进度条的线程中使用interrupted()方法。

package com.eistert.process;import java.awt.*;import javax.swing.*;public class InterruptedSwing extends JFrame {    /**     *      */    private static final long serialVersionUID = 1L;    Thread thread;    public static void main(String[] args) {        init(new InterruptedSwing(), 100, 100);    }    public InterruptedSwing() {        super();        final JProgressBar progressBar = new JProgressBar(); // 创建进度条        // 将进度条放置在窗体合适位置        getContentPane().add(progressBar, BorderLayout.NORTH);        progressBar.setStringPainted(true); // 设置进度条上显示数字        thread = new Thread(new Runnable() {            int count = 0;            public void run() {                while (true) {                    progressBar.setValue(++count); // 设置进度条的当前值                    try {                        Thread.sleep(1000); // 使线程休眠1000豪秒                        // 捕捉InterruptedException异常                    } catch (InterruptedException e) {                        System.out.println("当前线程序被中断");                        break;                    }                }            }        });        thread.start(); // 启动线程        thread.interrupt(); // 中断线程    }    public static void init(JFrame frame, int width, int height) {        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        frame.setSize(width, height);        frame.setVisible(true);    }}

在本实例中,由于调用了interrupted()方法,所以抛出了InterruptedException异常。

4、线程的优先级

每个线程的都具有各自的优先级,线程的优先级可以表明在程序中该线程的重要性,如果有很多线程处于就绪状态,系统会根据优先级来决定首先使那个线程进入运行状态。但这不意味着低优先级的线程得不到运行,而只是它运行的几率比较小,如垃圾回收线程的优先级就较低。

线程的优先级可以使用setPriority()方法调整,如果使用方法设置的优先级不再1-10之内,将产生IllegalArgumentException异常。

例4.在项目中创建PriorityTest类,该类实现了Runnable接口,创建4个进度条,分别由4个线程来控制,并且为这4个线程设置不同的优先级,本实例关键代码如下:

package com.eistert.process;import java.awt.*;import javax.swing.*;public class PriorityTest extends JFrame {    /**     *      */    private static final long serialVersionUID = 1L;    // 定义四个线程,属于饿汉式设计模式    private Thread threadA;    private Thread threadB;    private Thread threadC;    private Thread threadD;    //    public PriorityTest() {        getContentPane().setLayout(new GridLayout(4, 1));        // 定义4个进度条组件        final JProgressBar progressBar = new JProgressBar();        final JProgressBar progressBar2 = new JProgressBar();        final JProgressBar progressBar3 = new JProgressBar();        final JProgressBar progressBar4 = new JProgressBar();        // 将进度条放入窗口        getContentPane().add(progressBar);        getContentPane().add(progressBar2);        getContentPane().add(progressBar3);        getContentPane().add(progressBar4);        // 设置进度条显示数字字符        progressBar.setStringPainted(true);        progressBar2.setStringPainted(true);        progressBar3.setStringPainted(true);        progressBar4.setStringPainted(true);        // 分别实例化4个线程        threadA = new Thread(new MyThread(progressBar));        threadB = new Thread(new MyThread(progressBar2));        threadC = new Thread(new MyThread(progressBar3));        threadD = new Thread(new MyThread(progressBar4));        // 设置线程的优先级        setPriority("threadA", 5, threadA);        setPriority("threadB", 5, threadB);        setPriority("threadC", 4, threadC);        setPriority("threadD", 3, threadD);    }    // 定义设置线程的名称、优先级的方法    public static void setPriority(String threadName, int priority, Thread t) {        t.setPriority(priority); // 设置线程的优先级        t.setName(threadName); // 设置线程的名称        t.start(); // 启动线程    }    public static void main(String[] args) {        init(new PriorityTest(), 100, 100);    }    //设置窗口各种属性的方法    public static void init(JFrame frame, int width, int height) {        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        frame.setSize(width, height);        frame.setVisible(true);    }    private final class MyThread implements Runnable { // 定义一个实现Runnable接口的类        private final JProgressBar bar;        int count = 0;        private MyThread(JProgressBar bar) {            this.bar = bar;        }        public void run() { // 重写run()方法            while (true) {                bar.setValue(count += 10); // 设置滚动条的值每次自增10                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    System.out.println("当前线程序被中断");                }            }        }    }}

在本实例中定义了4个线程,这4个线程用于设置4个进度条的进度。这里定义了setPriority()方法,该方法设置了每个线程的优先级和名称等。虽然看这4个进度条好像是在一起滚动,但如果仔细观察还是可以看出细微差别,可以看出第一个进度条总是最先变化。由于threadA线程和threadB线程优先级最高,所以系统首先处理这两个线程,然后是threadC和threadD这两个线程。

**5、线程同步<**h3>

在但线程程序中,每次只能做一件事情,后面的事情需要等待前面的事情完成之后才可以进行,但是如果使用多线程程序,就会发生两个线程抢占资源的问题,Java中提供了线程同步的机制来防止资源访问的冲突;

(1)线程安全问题,实质上线程安全问题来源于两个线程同时存取单一对象的数据。

例5、在项目中创建ThreadSafeTest类,该类实现了Runnable接口,主要实现模拟火车站售票系统的功能。

package com.eistert.process;public class ThreadSafeTest implements Runnable {    int num = 10; // 设置当前总票数    public void run() {        while (true) {            if (num > 0) {                try {                    Thread.sleep(100);                } catch (Exception e) {                    e.printStackTrace();                }                System.out.println("tickets" + num--);            }        }    }    public static void main(String[] args) {        ThreadSafeTest t = new ThreadSafeTest(); // 实例化类对象        Thread tA = new Thread(t); // 以该类对象分别实例化4个线程        Thread tB = new Thread(t);        Thread tC = new Thread(t);        Thread tD = new Thread(t);        tA.start(); // 分别启动线程        tB.start();        tC.start();        tD.start();    }}

执行效果如下:

tickets10tickets9tickets8tickets7tickets6tickets5tickets4tickets3tickets2tickets1tickets0tickets-1tickets-2

这是由于同时创建了4个线程,这4个线程执行run()方法,在num变量为1时,线程1、线程2、线程3、线程4都对num变量有存储功能,当线程1执行run()方法时,还没有来得及做递减操作,就指定它调用sleep()方法进入就绪状态,这时线程2、线程3、线程4都进入了run()方法,发现num变量依然大于0,但此时线程1休眠时间已到,将num变量值递减,同时线程2、线程3、线程4也都对num变量进行递减操作,从而产生了负值;

6、线程的同步机制

(1)在Java中提供了同步机制,可以有效地防止资源冲突,同步机制使用synchronized关键字。

例6、在本实例中,创建类ThreadSafeTest.java,在该类中修改例5中的run()方法,把对num操作的代码设置在同步块中,本实例关键代码如下:

package com.eistert.process;public class ThreadSafeTest01 implements Runnable {    int num = 10;    public void run() {        while (true) {            synchronized (" ") {                if (num > 0) {                    try {                        Thread.sleep(1000);                    } catch (Exception e) {                        e.printStackTrace();                    }                    System.out.println("tickets" + --num);                }            }        }    }    public static void main(String[] args) {        ThreadSafeTest t = new ThreadSafeTest();        Thread tA = new Thread(t);        Thread tB = new Thread(t);        Thread tC = new Thread(t);        Thread tD = new Thread(t);        tA.start();        tB.start();        tC.start();        tD.start();    }}

(2)同步方法

同步方法就是在方法前面修饰synchronized关键字的方法,其语法如下:

synchronized void f(){//}

当某个对象调用了同步方法时,该对象上的其他同步方法必须等待该同步方法执行完毕后才能被执行。必须将每个能访问共享资源的的方法修饰为synchronized,否则就会出错。

例7、在项目中创建一个类文件,在该类文件定义同步方法。

package com.eistert.process;public class test02 {    private int num = 10;    public synchronized void doit() {        // 自定义同步方法        if (num > 0) {            try {                Thread.sleep(10);            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }            System.out.println("tickets" + --num);        }    }    public void run() {        while (true) {// 在run()方法中调用该同步方法            doit();        }    }    public static void main(String[] args) {        ThreadSafeTest t = new ThreadSafeTest();        Thread tA = new Thread(t);        Thread tB = new Thread(t);        Thread tC = new Thread(t);        Thread tD = new Thread(t);        tA.start();        tB.start();        tC.start();        tD.start();    }}

将共享资源的操作放置在同步方法中,运行结果与使用同步块的结果一致;

原创粉丝点击