Java语言基础:多线程
2011-04-02 01:55
555 查看
1. 有两种方法可以创建并运行一个线程:
继承Thread类并覆盖Run方法,Run中的代码就在另一个线程执行。
实现Runnable接口并传给新建的Thread类,实现类的Run方法即在另一个线程执行。
2.
后台线程(daemon)不属于程序不可或缺的一部分,当程序存在非后台线程时,程序就不会终止;而如果非后台线程都结束了,此时不管有没有后台线程,程序都可以随时终止。要设定一个线程为后台线程,可以调用Thread.setDaemon(true),在一个后台线程中创建其他线程,这些线程也自动被设为后台线程:
3.
在上面的例子中,如何让主线程等到后台线程结束时才结束呢?答案是调用后台线程的jion()方法,可以传入一个超时参数,如果不传则表示一直等到后台线程结束才返回:
4.
当两个线程同时读写同一份数据时,可能会引起资源冲突,这时就需要在并行机制之外,再提供一种同步机制,使对数据的读写可以有序的进行,Java对此的处理非常简单,就是使用synchronized关键字:
在普通方法前面加上synchronized,使得这个方法变成同步方法,先看下面例子,当主线程和工作线程同时调用synProc时,谁先调用到,谁就获得一个锁,另外一个只能等待,这样就保持在多线程环境下调用这个方法是有序的:
其实synchronized方法是在对象级别上的,每个对象都有一个锁,当调用它的任意一个同步方法时,就是去获得这个对象锁,当获得对象锁时,其他同步方法也不能同时被调用了,只能等这个方法执行完才可以被调用,下面代码清楚地说明这一点:
synchronized可以加到静态方法前面,每个类也有一个锁,调用同步静态方法就是获得类级别的锁,这使类中的所有同步静态方法变得有序,即只有一个同步静态方法被调用,其他的同步静态就只好等待它结束才能被调用。
synchronized还可以加在方法之内,称为同步块,如synchronized(obj){...}这样的语法,这里其实是获得obj对象的锁,因此Obj的同步方法也会受它影响,即对于Obj的同步块被执行时,Obj的同步方法必须等同步块执行完才能被调用:
5. 线程之间的等待与通知通过wait和notify,notifyAll来实现。
这三个方法都必须在对象的同步块中执行,否则运行期会出现IllegalMonitorStateException异常。
当线程A调用Obj.wait()时,A将被挂起,直到另一个线程B调用Obj.notify()或Obj.notifyAll(),A才被重新唤醒。
如果只有一个线程调用Obj.wait(),另一个线程只需要调用Obj.notify()即可;但如果有多个线程调用Obj.wait(),则另一个线程必须调用Obj.notifyAll()才可以将所有的线程唤醒。
下面代码演示了一个writer和多个reader的协作过程:
继承Thread类并覆盖Run方法,Run中的代码就在另一个线程执行。
class MyThread extends Thread { MyThread() { // 调用下面代码,线程开始运行 start(); } @Override public void run() { // 这里的代码是在线程中执行的 int i = 0; while (i < 20) { System.out.println(i); try { sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } ++i; } } } public class Main { public static void main(String[] args) { MyThread myThread = new MyThread(); } }
实现Runnable接口并传给新建的Thread类,实现类的Run方法即在另一个线程执行。
// 实现Runnable接口 public class Main implements Runnable { @Override public void run() { // 这里的代码是在线程中执行的 int i = 0; while (i < 20) { System.out.println(i); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } ++i; } } public static void main(String[] args) { Main runable = new Main(); new Thread(runable).start(); } }
2.
后台线程(daemon)不属于程序不可或缺的一部分,当程序存在非后台线程时,程序就不会终止;而如果非后台线程都结束了,此时不管有没有后台线程,程序都可以随时终止。要设定一个线程为后台线程,可以调用Thread.setDaemon(true),在一个后台线程中创建其他线程,这些线程也自动被设为后台线程:
class MyThread extends Thread { MyThread() { // 把该线程设为后台线程 setDaemon(true); // 调用下面代码,线程开始运行 start(); } @Override public void run() { // 这里的代码是在线程中执行的 int i = 0; while (i < 20) { System.out.println(i); try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ++i; } } } public class Main{ public static void main(String[] args) { MyThread mythread = new MyThread(); // mythread.run还没有执行完就结束程序了 } } // 程序只打印了0就结束了,而前面的例子程序会打印到19为止
3.
在上面的例子中,如何让主线程等到后台线程结束时才结束呢?答案是调用后台线程的jion()方法,可以传入一个超时参数,如果不传则表示一直等到后台线程结束才返回:
class MyThread extends Thread { MyThread() { // 把该线程设为后台线程 setDaemon(true); // 调用下面代码,线程开始运行 start(); } @Override public void run() { // 这里的代码是在线程中执行的 int i = 0; while (i < 20) { System.out.println(i); try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } ++i; } } } public class Main{ public static void main(String[] args) { MyThread mythread = new MyThread(); try { // 等待后台线程结束 mythread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
4.
当两个线程同时读写同一份数据时,可能会引起资源冲突,这时就需要在并行机制之外,再提供一种同步机制,使对数据的读写可以有序的进行,Java对此的处理非常简单,就是使用synchronized关键字:
在普通方法前面加上synchronized,使得这个方法变成同步方法,先看下面例子,当主线程和工作线程同时调用synProc时,谁先调用到,谁就获得一个锁,另外一个只能等待,这样就保持在多线程环境下调用这个方法是有序的:
class MyThread extends Thread { MyThread() { start(); } @Override public void run() { synProc(); } synchronized public void synProc(){ int count = 0; while (count++ < 10) { try { sleep(100); System.out.println("synProc: " + count); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Main{ public static void main(String[] args) { MyThread thread = new MyThread(); thread.synProc(); } } // 输出结果是: synProc: 1 synProc: 2 synProc: 3 synProc: 4 synProc: 5 synProc: 6 synProc: 7 synProc: 8 synProc: 9 synProc: 10 synProc: 1 synProc: 2 synProc: 3 synProc: 4 synProc: 5 synProc: 6 synProc: 7 synProc: 8 synProc: 9 synProc: 10
其实synchronized方法是在对象级别上的,每个对象都有一个锁,当调用它的任意一个同步方法时,就是去获得这个对象锁,当获得对象锁时,其他同步方法也不能同时被调用了,只能等这个方法执行完才可以被调用,下面代码清楚地说明这一点:
class MyThread extends Thread { MyThread() { start(); } @Override public void run() { synProc(); } synchronized public void synProc(){ int count = 0; while (count++ < 10) { try { sleep(100); System.out.println("synProc: " + count); } catch (InterruptedException e) { e.printStackTrace(); } } } synchronized public void synProc2() { int count = 0; while (count++ < 10) { try { sleep(100); System.out.println("synProc2: " + count); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Main{ public static void main(String[] args) { MyThread thread = new MyThread(); thread.synProc2(); } } // 输出 synProc2: 1 synProc2: 2 synProc2: 3 synProc2: 4 synProc2: 5 synProc2: 6 synProc2: 7 synProc2: 8 synProc2: 9 synProc2: 10 synProc: 1 synProc: 2 synProc: 3 synProc: 4 synProc: 5 synProc: 6 synProc: 7 synProc: 8 synProc: 9 synProc: 10
synchronized可以加到静态方法前面,每个类也有一个锁,调用同步静态方法就是获得类级别的锁,这使类中的所有同步静态方法变得有序,即只有一个同步静态方法被调用,其他的同步静态就只好等待它结束才能被调用。
synchronized还可以加在方法之内,称为同步块,如synchronized(obj){...}这样的语法,这里其实是获得obj对象的锁,因此Obj的同步方法也会受它影响,即对于Obj的同步块被执行时,Obj的同步方法必须等同步块执行完才能被调用:
class MyThread extends Thread { MyThread() { start(); } @Override public void run() { synProc(); } public void synProc(){ int count = 0; synchronized (this) { do { try { sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("synProc :" + count); } while (count++ < 3); } } synchronized public void synProc2() { int count = 0; do { try { sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("synProc2 :" + count); } while (count++ < 3); } } public class Main{ public static void main(String[] args) { MyThread thread = new MyThread(); thread.synProc2(); } } // 输出: synProc2 :0 synProc2 :1 synProc2 :2 synProc2 :3 synProc :0 synProc :1 synProc :2 synProc :3
5. 线程之间的等待与通知通过wait和notify,notifyAll来实现。
这三个方法都必须在对象的同步块中执行,否则运行期会出现IllegalMonitorStateException异常。
当线程A调用Obj.wait()时,A将被挂起,直到另一个线程B调用Obj.notify()或Obj.notifyAll(),A才被重新唤醒。
如果只有一个线程调用Obj.wait(),另一个线程只需要调用Obj.notify()即可;但如果有多个线程调用Obj.wait(),则另一个线程必须调用Obj.notifyAll()才可以将所有的线程唤醒。
下面代码演示了一个writer和多个reader的协作过程:
class Writer extends Thread { private char chr; @Override public void run() { for (char c = 'A'; c <= 'Z'; ++c){ chr = c; // 通知Reader可以读了 synchronized (this) { notifyAll(); } // 过会儿再写 try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public char getChar() { return chr; } } class Reader extends Thread { private Writer writer; Reader(Writer writer) { this.writer = writer; start(); } @Override public void run() { while (true){ // 等待Writer写 synchronized (writer) { try { // 此处Reader线程将被挂起 writer.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 读取Writer写的内容 char chr = writer.getChar(); System.out.print(chr + " "); // 读到Z即结束 if (chr == 'Z') { System.out.println(); break; } } } } public class Main{ public static void main(String[] args) { Writer writer = new Writer(); Reader reader1 = new Reader(writer); Reader reader2 = new Reader(writer); // 开始 writer.start(); } } // 最后的输出是: A A B B C C D D E E F F G G H H I I J J K K L L M M N N O O P P Q Q R R S S T T U U V V W W X X Y Y Z Z
相关文章推荐
- java语言基础(89)——多线程(线程控制)
- java语言基础(88)——多线程(实现多线程的四种方式)
- Java语言基础-多线程
- [黑马程序员]--Java语言基础-多线程
- Java语言基础:多线程
- Java语言基础:多线程
- 黑马程序员——Java语言基础——04.多线程(2)线程间通信
- java语言基础(93)——多线程中的死锁问题
- 黑马程序员-Java语言基础– 多线程 第11天
- java语言基础(91)——多线程(同步方法解决线程安全问题)
- 黑马程序员-Java语言基础– 多线程 第12天
- java语言基础(90)——多线程(同步代码块解决多线程售电影票时的负票问题)
- 【Java 语言】Java 多线程 一 ( 线程基础 : 线程启动 | 线程停止 | 线程暂停 | 线程优先级 | 守护线程)
- java语言基础(97)——匿名内部类实现多线程
- 黑马程序员——Java语言基础——04.多线程(1)多线程概念
- 黑马程序员——Java语言基础:多线程
- Java语言基础之super关键字
- Java语言基础(七) : 循环结构之 while 循环
- 黑马程序员_Java语言基础组成
- Java 多线程(一) 基础知识与概念