线 程
线程是一个程序内部的顺序控制流
线程和进程
- 每个进程都有独立的代码和数据空间,进程切换的开销大
- 线程:轻量的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(pc),线程切换的开销小.
- 多进程:在操作系统中能同时运行多个任务(程序);
- 多线程:在同一应用程序中有多个顺序流同时执行.
线程的概念模型
- 虚拟的cpu,由java.lang.Thread类封装和虚拟;
- cpu所执行的代码,传递给Thread类对象;
- cpu所处理的数据,传递给Thread类对象
创建线程
java的线程是通过java.lang.Thread类实现的.
每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。
public class TestThread1 {
public static void main(String args[]) {
Runner1 r = new Runner1();
Thread t = new Thread(r);
t.start();
}
}
class Runner1 implements Runnable {
public void run() {
for(int i=0; i<30; i++) {
System.out.println("No. " + i);
}
}
}
多线程
java中引入线程机制的目的在于实现多线程(Multi-Thread)
public class TestThread2 {
public static void main(String args[]) {
Runner2 r = new Runner2();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class Runner2 implements Runnable {
public void run() {
for(int i=0; i<20; i++) {
String s = Thread.currentThread().getName();
System.out.println(s + ": " + i);
}
}
}
创建线程第二种方式
直接继承Thread类创建线程
public class TestThread3 {
public static void main(String args[]){
Thread t = new Runner3();
t.start();
}
}
class Runner3 extends Thread {
public void run() {
for(int i=0; i<30; i++) {
System.out.println("No. " + i);
}
}
}
两种方式的比较
一.使用Runnable接口创建线程:
- 可以将cpu,代码和数据分开,形成清晰的模型;
- 线程体run()方法所在的类还可以从其他类继承一些有用的属性或方法;
- 并有利于保持程序风格的一致性.
二.直接继承Thread类创建线程:
- Thread子类无法再从其他类继承
- 编写简单,run()方法的当前对象就是线程对象,可直接操纵.
后台线程
相关基本概念:
- 后台处理(Background Processing)
- 后台线程(Background Thread/Daemon Thread)
- 用户线程(User Thread)
- 主线程(Main Thread)
- 子线程(Sub Thread)
Thread类提供的相关方法:
- public final boolean isDaemon()
- public final void setDaemon(Boolean on)
public class TestDaemonThread {
public static void main(String args[]){
Thread t1 = new MyRunner(10);
t1.setName("用户线程t1");
t1.start();
Thread t2 = new MyRunner(100);
t2.setDaemon(true);
t2.setName("后台线程t2");
t2.start();
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName() + ": " + i);
}
System.out.println("主线程结束!");
}
}
class MyRunner extends Thread {
private int n;
public MyRunner(int n){
this.n = n;
}
public void run() {
for(int i=0; i<n; i++) {
System.out.println(this.getName() + ": " + i);
}
System.out.println(this.getName() + "结束!");
}
}
线程的生命周期
- 新建状态
- 就绪状态
- 运行状态
- 阻塞状态
- 终止状态
线程优先级
线程的优先级用数字来表示,范围从1到10.
主线程的缺省优先级是5,子线程的优先级默认与其父线程相同.
Thread类提供的相关方法:
- public final int getPriority()
- public final void setPriority(int newPriority)
相关静态整型常量:
- Thread.MIN_PRIORITY=1
- Thread.MAX_PRIORITY=10
- Thread.NORM_PRIORITY=5
public class TestPriority {
public static void main(String args[]){
System.out.println("线程名\t优先级");
Thread current = Thread.currentThread();
System.out.print(current.getName() + "\t");
System.out.println(current.getPriority());
Thread t1 = new Runner();
Thread t2 = new Runner();
Thread t3 = new Runner();
t1.setName("First");
t2.setName("Second");
t3.setName("Third");
t2.setPriority(Thread.MAX_PRIORITY);
t3.setPriority(8);
t1.start();
t2.start();
t3.start();
}
}
class Runner extends Thread {
public void run() {
System.out.print(this.getName() + "\t");
System.out.println(this.getPriority());
}
}
线程串行化
在多线程程序中,如果在一个线程运行的过程中药用到另一个线程的运行结果,则可进行线程的串型化处理.
Thread类提供的相关方法:
- public final void join()
- public final void join(long millis)
- public final void join(long millis,int nanos)
public class TestJoin {
public static void main(String args[]){
MyRunner r = new MyRunner();
Thread t = new Thread(r);
t.start();
try{
t.join();
}catch(InterruptedException e){
e.printStackTrace();
}
for(int i=0;i<50;i++){
System.out.println("主线程:" + i);
}
}
}
class MyRunner implements Runnable {
public void run() {
for(int i=0;i<50;i++) {
System.out.println("SubThread: " + i);
}
}
}
线程休眠
线程休眠——暂停执行当前运行中的线程,使之进入阻塞状态,待经过指定的"延迟时间"后再醒来并转入到就绪状态.
Thread类提供的相关方法:
- public static void sleep(long millis)
- public static void sleep(long millis,int nanos)
import java.awt.Color;
import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.swing.*;
public class DigitalClock{
public static void main(String[] args){
JFrame jf = new JFrame("Clock");
JLabel clock = new JLabel("Clock");
clock.setHorizontalAlignment(JLabel.CENTER);
jf.add(clock,"Center");
jf.setSize(140,80);
jf.setLocation(500,300);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
Thread t = new MyThread(clock);
t.start();
}
}
class MyThread extends Thread{
private JLabel clock;
public MyThread(JLabel clock){
this.clock = clock;
}
public void run(){
while(true){
clock.setText(this.getTime());
try{
Thread.sleep(1000); //this.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public String getTime(){
Calendar c = new GregorianCalendar();
String time = c.get(Calendar.YEAR) + "-" +
(c.get(Calendar.MONTH) + 1) + "-" +
c.get(Calendar.DATE) + " " ;
int h = c.get(Calendar.HOUR_OF_DAY);
int m = c.get(Calendar.MINUTE);
int s = c.get(Calendar.SECOND);
String ph = h<10 ? "0":"";
String pm = m<10 ? "0":"";
String ps = s<10 ? "0":"";
time += ph + h + ":" + pm + m + ":" + ps + s;
return time;
}
}
线程让步
线程——让运行中的线程主动放弃当前获得的CPU处理机会,但不是该线程阻塞,而是使之转入就绪状态.
Thread类提供的相关方法:
- public static void yield()
import java.util.Date;
public class TestYield{
public static void main(String[] args){
Thread t1 = new MyThread(false);
Thread t2 = new MyThread(true);
Thread t3 = new MyThread(false);
t1.start();
t2.start();
t3.start();
}
}
class MyThread extends Thread{
private boolean flag;
public MyThread(boolean flag){
this.flag = flag;
}
public void setFlag(boolean flag){
this.flag = flag;
}
public void run(){
long start = new Date().getTime();
for(int i=0;i<500;i++){
if(flag)
Thread.yield();
System.out.print(this.getName() + ": " + i + "\t");
}
long end = new Date().getTime();
System.out.println("\n" + this.getName() + "执行时间: " + (end - start) + "毫秒");
}
}
线程挂起和恢复
线程挂起——暂时停止当前运行中的线程,使之转入阻塞状态,并且不会自动恢复运行.
线程恢复——使得一个已挂起的线程恢复运行.
Thread类提供的相关方法:
- public final void suspend()
- public final void resume()
利用线程挂起/恢复机制实现倒计时器
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JButton;
public class TestSuspend{
public static void main(String[] args){
JFrame jf = new JFrame("Timer");
JButton pause = new JButton("Pause");
JLabel clock = new JLabel("Timer");
clock.setBackground(Color.GREEN);
clock.setOpaque(true);
clock.setHorizontalAlignment(JLabel.CENTER);
jf.add(clock,"Center");
jf.add(pause,"North");
jf.setSize(140,80);
jf.setLocation(500,300);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
MyThread mt = new MyThread(clock,10000);
mt.start();
MyListener ml = new MyListener(clock,mt);
pause.addActionListener(ml);
}
}
class MyThread extends Thread{
private JLabel clock;
private long time;
private long end;
public MyThread(JLabel clock,long time){
this.clock = clock;
this.time = time;
}
public void init(){
long start = new Date().getTime();
end = start + time;
}
public void run(){
this.init();
while(true){
long now = new Date().getTime();
time = end - now;
if(time > 0){
String s = this.convert(time);
clock.setText(s);
}else{
break;
}
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
}
clock.setText("时间到!");
clock.setBackground(Color.RED);
}
public String convert(long time){
long h = time / 3600000;
long m = (time % 3600000) / 60000;
long s = (time % 60000) / 1000;
long ms = (time % 1000) / 10;
String ph = h<10 ? "0":"";
String pm = m<10 ? "0":"";
String ps = s<10 ? "0":"";
String pms = ms<10 ? "0":"";
String txt = ph + h + ":" + pm + m + ":" + ps + s + "." + pms + ms;
return txt;
}
}
class MyListener implements ActionListener{
private JLabel clock;
private MyThread mt;
private boolean running= true;
public MyListener(JLabel clock,MyThread mt){
this.clock = clock;
this.mt = mt;
}
public void actionPerformed(ActionEvent e){
if(!mt.isAlive())
return;
JButton jb = (JButton)(e.getSource());
if(running){
jb.setText("Replay");
clock.setBackground(Color.YELLOW);
mt.suspend();
}else{
jb.setText("Pause");
clock.setBackground(Color.green);
mt.init();
mt.resume();
}
running = !running;
}
}
线程控制基本方法
isAllve() 判断线程是否还"活"着,即线程是否还未终止
getPriority() 获得线程的优先级数值
setPriority() 设置线程的优先级数值
sleep() 将当前线程睡眠指定毫秒数
join() 调用某线程的该方法,将当前线程与该线程"合并",即等待该线程结束,再恢复当前线程的运行.
yield() 让出cpu,当前线程进入就绪队列等待调度
suspend()/resume() 挂起和恢复线程
wait() 当前线程进入对象的wait pool
notify()和notifyAll() 唤醒对象的wait pool中的一个/所有等待线程
互斥锁
- 在java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性.
- 每个对象都对应于一个可称为"互斥锁"的标记,这个标记用来保证在任意时刻,只能有一个线程访问该对象.
- 关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任意时刻只能由一个线程访问.
synchronized用法
用于方法声明中,标明整个方法为同步方法
public synchronized void push(char c){
data[idx] = c;
idx++;
}
public char pop(){
//其他代码
synchronized(this){
idx--;
return data[idx];
}
//其他代码
}
线程死锁
并发运行的多个线程间彼此等待,都无法运行的状态称为线程死锁.
public class TestDeadLock{
public static void main(String args[]){
StringBuffer sb = new StringBuffer("ABCD");
MyThread t = new MyThread(sb);
t.start();
synchronized(sb){
try{
t.join();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(sb);
}
System.out.println("Main thread is over!");
}
}
class MyThread extends Thread{
private StringBuffer sb;
public MyThread(StringBuffer sb){
this.sb = sb;
}
public void run(){
synchronized(sb){
sb.reverse();
}
System.out.println("Sub thread is over!");
}
}
---------------------------------------------------------------------------------------------------------------------------------
public class TestDeadLock2{
public static void main(String args[]){
char[] a = {'A','B','C'};
char[] b = {'D','E','F'};
MyThread t1 = new MyThread(a,b);
MyThread t2 = new MyThread(b,a);
t1.start();
t2.start();
}
}
class MyThread extends Thread{
private char[] source;
private char[] dest;
public MyThread(char[] source,char[] dest){
this.source = source;
this.dest = dest;
}
public void run(){
synchronized(source){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
synchronized(dest){
System.arraycopy(source,0,dest,0,source.length);
System.out.println(dest);
}
}
}
}
线程同步通信
为避免死锁,在线程进入阻塞状态时应尽量释放其锁定的资源,以为其他的线程提供运行的机会.
相关方法:
- public final void wait()
- public final void notify()
- public final void notfyAll()
生产者——消费者问题
public class SyncStack{ //支持多线程同步操作的堆栈的实现
private int index = 0;
private char []data = new char[6];
public synchronized void push(char c){
while(index == data.length){
try{
this.wait();
}catch(InterruptedException e){}
}
this.notify();
data[index] = c;
index++;
System.out.println("produced:"+c);
}
public synchronized char pop(){
while(index ==0){
try{
this.wait();
}catch(InterruptedException e){}
}
this.notify();
index--;
System.out.println("消费:"+data[index]);
return data[index];
}
}
public class SyncTest{
public static void main(String args[]){
SyncStack stack = new SyncStack();
Runnable p=new Producer(stack);
Runnable c = new Consumer(stack);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
class Producer implements Runnable{
SyncStack stack;
public Producer(SyncStack s){
stack = s;
}
public void run(){
for(int i=0; i<20; i++){
char c =(char)(Math.random()*26+'A');
stack.push(c);
try{
Thread.sleep((int)(Math.random()*300));
}catch(InterruptedException e){
}
}
}
}
class Consumer implements Runnable{
SyncStack stack;
public Consumer(SyncStack s){
stack = s;
}
public void run(){
for(int i=0;i<20;i++){
char c = stack.pop();
try{
Thread.sleep((int)(Math.random()*500));
}catch(InterruptedException e){
}
}
}
}
线程间数据传输
使用管道流实现线程间数据传输
import java.io.*;
public class Test{
public static void main(String args[]){
PipedInputStream pin = new PipedInputStream();
PipedOutputStream pout = new PipedOutputStream();
try{
pin.connect(pout);
}catch(IOException e){
e.printStackTrace();
}
Thread t1 = new Sender(pout);
Thread t2 = new Receiver(pin);
t1.start();
t2.start();
}
}
class Sender extends Thread{
private DataOutputStream dos;
public Sender(PipedOutputStream p){
dos = new DataOutputStream(p);
}
public void run(){
try{
dos.writeUTF("HelloWorld");
dos.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
class Receiver extends Thread{
private DataInputStream dis;
public Receiver(PipedInputStream p){
dis = new DataInputStream(p);
}
public void run(){
try{
System.out.println(dis.readUTF());
dis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
类的同步性与线程安全
验证同步类的线程安全性
import java.util.Vector;
public class Test{
public static void main(String args[]){
Vector<String> v = new Vector<String>();
v.addElement("Tom");
v.addElement("Billy");
v.addElement("Kessy");
v.addElement("Mr Brown");
v.addElement("Kity");
v.addElement("Johnson");
v.addElement("Nancy");
v.addElement("Scott");
v.addElement("Ruby");
MyThread1 mt1 = new MyThread1(v);
MyThread2 mt2 = new MyThread2(v);
mt1.start();
mt2.start();
}
}
class MyThread1 extends Thread{
private Vector<String> v;
public MyThread1(Vector<String> v){
this.v = v;
}
public void run(){
synchronized(v){
int total = v.size();
for(int i=0;i<total;i++){
String s = v.elementAt(i);
StringBuffer sb = new StringBuffer(s);
s = sb.reverse().toString();
v.setElementAt(s,i);
System.out.println(v.elementAt(i));
}
}
}
}
class MyThread2 extends Thread{
private Vector<String> v;
public MyThread2(Vector<String> v){
this.v = v;
}
public void run(){
v.clear();
}
}
定时器
使用定时器实现数字时钟功能
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class TestTimer{
public static void main(String args[]){
JFrame jf = new JFrame("Clock");
JLabel clock = new JLabel("Clock");
clock.setHorizontalAlignment(JLabel.CENTER);
jf.add(clock,"Center");
jf.setSize(140,80);
jf.setLocation(500,300);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
Timer t = new Timer();
TimerTask tt = new MyTask(clock);
t.schedule(tt,0,1000);
}
}
class MyTask extends TimerTask{
private JLabel clock;
public MyTask(JLabel clock){
this.clock = clock;
}
public void run(){
clock.setText(this.getTime());
}
public String getTime(){
Calendar c = new GregorianCalendar();
String time = c.get(Calendar.YEAR) + "-" +
(c.get(Calendar.MONTH) + 1) + "-" +
c.get(Calendar.DATE) + " " ;
int h = c.get(Calendar.HOUR_OF_DAY);
int m = c.get(Calendar.MINUTE);
int s = c.get(Calendar.SECOND);
String ph = h<10 ? "0":"";
String pm = m<10 ? "0":"";
String ps = s<10 ? "0":"";
time += ph + h + ":" + pm + m + ":" + ps + s;
return time;
}
}