Java之多线程简介

要了解线程,就必须了解进程,那么什么是进程?
进程:程序在处理机上执行时所发生的活动叫进程,进程是执行中的程序,是活动的实体,进程是“自包容”的运行程序,有自己的地址空间,简单的说,比如我的电脑打开了ppt和word2个程序,则这就相当于2个进程.
线程:线程是轻量级进程,是CPU使用的基本单元
线程属于某一个进程,一个进程中拥有一个或多个线程
多个线程共享同一个进程资源
 多任务处理有两类
 基于进程:计算机同时运行多个进程
 基于线程:一个进程包含多个线程
 特点:
 各个进程需要分配它们自己独立的地址空间
 进程间调用涉及的开销比线程间通信多
 多个线程可共享相同的地址空间并且共同分享同一个进程
 线程间的切换成本比进程间切换成本低
 进程是重量级的、线程是轻量级的
JVM中的线程

  1. 当操作系统启动(javac、java、javaw等命令)JVM时就创建了一个进

  2. JVM进程内至少包含了两个线程:main方法和垃圾回收线程,它们
    称为主线程,有时它们还会启动其他线程,它们必须最后完成执行,
    执行各种关闭、释放动作
    在Java中,实现线程的方式有3种,
    (1)声明一个 Thread 类的子类,并覆盖 run() 方法

     public class SampleThread extends Thread {
     		public void run( ) {/* 覆盖该方法*/ }
     		 }
      }
    

(2)声明一个实现 Runnable 接口的类,并实现 run() 方法

		public class SampleThread implements Runnable{
    				public void run( ) {
    				/* 实现该方法*/
    				 }
    	}

(3)实现Callable接口,实现他的call方法;

import java.util.concurrent.Callable;

public class Thread<V> implements Callable<V>{

	public V call() throws Exception {
		// TODO Auto-generated method stub
		return null;
	}
}
Callable<V> oneCallable = new SomeCallable<V>();   
//由Callable<Integer>创建一个FutureTask<Integer>对象:   
FutureTask<V> oneTask = new FutureTask<V>(oneCallable);   
//注释:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。 
  //由FutureTask<Integer>创建一个Thread对象:   
Thread oneThread = new Thread(oneTask);   
oneThread.start();   
//至此,一个线程就创建完成了。

值得注意的是,第一种直接继承Thread类的方法,是直接重写了run方法,也就是将自己变成了线程; 而第二种方式自己本身并不是线程,它实现runnable接口后, 实现了其中的run方法,只是拥有了规定一个空线程如何run的能力.
2中方式的实例代码:
先定义2个线程; MyT1和 MyT2(请自己新建3个类将下面代码分开)

public class MyT1 extends Thread {
	@Override
	public void run() {
		for(int i = 0; i < 1000; i++) {
			System.out.println("aaaaa");
		}
	}
}
public class MyT2 extends Thread {
    	@Override
    	public void run() {
    		for(int i = 0; i < 1000; i++) {
    			System.out.println("bbbbb");
    		}
    	}
    }
public class Run {
	public static void main(String[] args) {
		System.out.println("main开始!");
		MyT1 t1 = new MyT1();
		MyT2 t2 = new MyT2();
		//设置线程优先级
		t1.setPriority(1);
		t2.setPriority(10);
		t1.start();
		t2.start();
		System.out.println("main结束!");
	}
}

第二种方式:
先定义2个线程; MyT1和 MyT2(请自己新建3个类将下面代码分开)

public class MyT1 implements Runnable {
	@Override
	public void run() {
		try {
			for(int i = 0; i < 10; i++) {
				System.out.println("aaaaa");
				Thread.currentThread().sleep(1000);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
//此处MyT2和MyT1采用不同的写法
public class MyT2 implements Runnable {
	private Thread t = null;
	public MyT2() {
		this.t = new Thread(this);
		this.t.start();
	}
	@Override
	public void run() {
		try {
			for(int i = 0; i < 10; i++) {
				System.out.println("bbbbb");
				Thread.currentThread().sleep(2000);
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
public class Run {
	public static void main(String[] args) {
		System.out.println("main开始!");
		//MyT1和MyT2的=run的方式也会有所不同
		Thread t1 = new Thread(new MyT1());
		t1.start();
		new MyT2();
		System.out.println("main结束!");
	}
}

线程的状态
新建态(NEW) :至今尚未启动的线程处于这种状态
 新创建的但未调用Thread.start()方法的线程处于该状态
运行态 (RUNNABLE) :可运行线程的线程状态,已经调用start方法
 处于可运行状态的某一线程正在 Java 虚拟机中运行,但它 可能正在等待操作系统中的其他资源,比如处理器
锁定态(等待态) (BLOCKED) :受阻塞线程的线程状态
 处于受阻塞状态的线程正在等待监视器锁,以便进入一个同
步的块/方法,或者在调用Object.wait()之后再次进入同步 的块/方法
 锁定态又分为定时锁定(给定时间锁定)和不定时锁定(调用sleep方法)
中止态(TERMINATED):已终止线程的线程状态。线程 已经结束执行
二: 线程中的方法
在这里插入图片描述
在这里插入图片描述
1.sleep()方法
在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。不推荐使用。sleep()使当前线程进入阻塞状态,在指定时间内不会执行。
2.wait()方法
在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。
唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常。waite()和notify()必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。
3.yield方法
暂停当前正在执行的线程对象。yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。yield()只能使同优先级或更高优先级的线程有执行的机会。
4.join方法
等待该线程终止。等待调用join方法的线程结束,再继续执行。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测。
三:线程优先级(线程抢占CPU的能力)
如果不设置优先级,默认为5,Java提供1~10的优先级,1最低,10最高
设置优先级方法:

final void setPriority(int newp) //: 修改线程的当前优先级
final int getPriority() //: 返回线程的优先级

四: 线程同步
有时两个或多个线程可能会试图同时访问一个资源例如,一个线程可能尝试从一个文件中读取数据,而另一个线程则尝试在同一文件中修改数据在此情况下,数据可能会变得不一致;为了确保在任何时间点一个共享的资源只被一个线程使用,必须使用“同步”同步是指同时只能有一个线程访问同一个资源.
完成同步的2中方式:
(1)同步方法,

synchronized void methodA() { }

(2)同步块

 synchronized(object) {
		//要同步的语句
}

同步示例:
首先,新建3个类PrintM Student 和 Run
PrintM:

public class PrintM {
	public void print(String info) {
		System.out.print("[" + info);
		try {
			Thread.currentThread().sleep(1000);
			System.out.println("]");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

Student :

public class Student extends Thread {
	private String name = null;
	private PrintM pm = null;
	public Student() {
		// TODO Auto-generated constructor stub
	}
	public Student(String n, PrintM p) {
		this.name = n;
		this.pm = p;
	}
	@Override
	public void run() {
		synchronized(this.pm) {
			this.pm.print(this.name);
		}
	}
}

除了这种方式(同步块),因为我们用一台打印机printM打印东西,故还可以将printM中的print方法加上同步块关键字synchronized,(也就是同步方法)
Run:

 public class Run {
	public static void main(String[] args) {
		PrintM pm = new PrintM();
		Student s1 = new Student("rose", pm);
		Student s2 = new Student("jack", pm);
		Student s3 = new Student("peter", pm);	
		s1.start();
		s2.start();
		s3.start();
	}
}

何时用同步方法?同步块?
只有一种情况我们建议使用同步方法: 这个类是自己写的,并且这个类中的这个方法永远都要做同步处理,其他情况基本都使用同步块,有利于提高效率!
五: wait-notify机制(让多个线程协作做事情)
Java中线程间通讯只提供2种/操作,等wait()和叫醒notify((只通知离自己最近的线程))和notifyAll()(所有线程都唤醒做事情,但只能一个优先级最高的做,其余线程仍然等待)因为:
这三个方法只有在同步方法中才能用(这些方法是作为 Object 类中的 final 方法实现的)
示例代码:(生产(面包)数字,消费数字)
Number类:

public class Number {
	private int num = -1;
	private boolean numOk = false;
	/**
	 * 生成数字方法
	 * @param n 生成的数字
	 */
	public synchronized void putNum(int n) {
		if(numOk) {
			try {
				wait();
			} catch (InterruptedException e) {
				System.out.println("putNum()方法发生线程终断异常!");
				e.printStackTrace();
			}
		}
		this.num = n;
		this.numOk = true;
		System.out.println("生成数字:" + this.num);
//		try {
//			Thread.sleep(1000);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		notifyAll();
	}
	/**
	 * 得到数字方法
	 * @return 得到的数字
	 */
	public synchronized void getNum() {
		if(!numOk) {
			try {
				wait();
			} catch (InterruptedException e) {
				System.out.println("getNum()方法发生线程终断异常!");
				e.printStackTrace();
			}
		}
		System.out.println("得到数字:" + this.num);
		this.numOk = false;
//		try {
//			Thread.sleep(1000);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		notifyAll();
	}
}

produce类:

/**
 * 生成数字线程
 */
public class Produce implements Runnable {
	private int i = 0;
	private Number number = null;
	
	public Produce(Number n) {
		this.number = n;
		Thread t = new Thread(this, "produce");
		t.start();
	}
	public void run() {
		for(int j = 0; j < 10; j++) {
			this.number.putNum((i++) * 10);
		}
	}
}
Consume类:


  /**
 * 消费数字线程
 */
public class Consume implements Runnable {
	private Number number = null;
	
	public Consume(Number n) {
		this.number = n;
		Thread t = new Thread(this, "consume");
		t.start();
	}
	public void run() {
		for(int i = 0; i < 10; i++) {
			this.number.getNum();	
		}
	}
}

NumberRun类:

   public class NumberRun {

	public static void main(String[] args) {
		Number number = new Number();
		new Produce(number);
		new Consume(number);
	}
}

最后,送给大家一个用线程做的小玩意:javaswing+线程 大家可以运行玩玩哈!!
3个类:
BallPanel类:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
 * 绘制界类
 * @author IBMT40
 *
 */
public class BallPanel extends JFrame {
	private JPanel mainPanel = new JPanel();
	private JPanel operatePanel = new JPanel();
	
	public BallPanel() {
		Container c = this.getContentPane();
		c.setLayout(new BorderLayout());
		this.mainPanel.setBackground(Color.BLACK);
		c.add(this.mainPanel, BorderLayout.CENTER);
		JButton b1 = new JButton("开始");
		b1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Ball b = new Ball(mainPanel);
				b.start();
			}	
		});
		JButton b2 = new JButton("退出");
		b2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		this.operatePanel.add(b1);
		this.operatePanel.add(b2);
		c.add(this.operatePanel, BorderLayout.SOUTH);
		
		this.setSize(400, 400);
		this.setTitle("ball");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setVisible(true);
	}
}

Ball类:

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
/**
 * 绘制小球线程
 * @author IBMT40
 *
 */
public class Ball extends Thread {
	
	private JPanel mainPanel = null;
	private int x = 0;
	private int y = 0;
	private int dx = 5;
	private int dy = 5;
	private int width = 20;
	private int height = 20;
	
	public Ball(JPanel p) {
		this.mainPanel = p;
	}
	
	public void run() {
		init();
		while(true) {
			try {
				Thread.sleep(20);
				Graphics g = this.mainPanel.getGraphics();
				g.setColor(Color.WHITE);
				//g.setXORMode(this.mainPanel.getBackground());
				g.setXORMode(Color.BLACK);
				
				g.fillOval(this.x, this.y, this.width, this.height);
				x = x + dx;
				y = y + dy;
				if(x > this.mainPanel.getWidth() - this.width) {
					dx = -dx;
				}
				if(y > this.mainPanel.getHeight() - this.height) {
					dy = -dy;
				}
				if(x < 0) {
					x = 0;
					dx = -dx;
				}
				if(y < 0) {
					y = 0;
					dy = -dy;
				}
				g.fillOval(x, y, this.width, this.height);
				g.dispose();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	private void init(){
		Graphics graphics = this.mainPanel.getGraphics();
		graphics.setColor(Color.WHITE);
		graphics.fillOval(x, y, this.width, this.height);
		graphics.dispose();
	}
}

BallRun类:

 /**
 * 主程序
 * @author IBMT40
 *
 */
public class BallRun {
	public static void main(String[] args) {
		new BallPanel();
	}
}

猜你喜欢

转载自blog.csdn.net/lq1759336950/article/details/97826074