java的多线程是java基础的一个重要的知识点,本人能力有限,而且语言表达能力也不太好,在此写下为了更好的巩固知识,以后复习也能够快速的理解。
线程的概念:线程是系统调度和分派的基本单位。
引入线程的目的:单线程进程调度时存在进程时空开销大、进程通信代价大、进程并发颗粒粗、不适合并行计算等问题。操作系统支持多线程进程,能够减少程序并发时所付出的时间和空间的开销,是的并发颗粒度更细,并发性更好。(引用Java程序设计的一段话)
线程的生命周期:
Threadh和Runnable
Runnable是一个接口,它只有一个抽象方法run(),run()方法是线程启动后执行的方法。Thread类具有封装线程的能力,能够创建,管理线程。这里有两种创建线程的方式:
1.new Thread(public void run(){...}).start();
2.new Thread(new Runnable(public void run(...))).start();
第二种方法通过隐式创建Runnable的实现类并将对象作为参数,其主要是为Thread线程对象提供run方法。通常情况下,使用一个类继承Thread类然后实例化来创建进程,缺点是不能多继承,扩展性比较差。使用一个类实现Runnable作为Runnabled的子类,但要创建线程同时还要声明Thread对象。
Thread中的各种状态
其中TIME_WAITING和WAITING的区别是TIME_WAITING是有时间确定的,例如:sleep(1000),wait(1000);WAITIGN状态是等待时间不确定,例如wait()。这里还需要说明的就是,按照线程的生命周期,应该是有就绪态的,这里没有就绪态的原因是处于就绪态的线程条件都已经满足了,正等待处理器的调用,然而处理器的调度是不可预知,所以无法判断线程是处于就绪态还是运行态,一般来说线程对象调用start()方法就是在RUNNABLE状态了。
Java线程常用方法:
1.setPriority(int newPriority);//更改线程的优先级
Thread声明了3个表示优先级的共有静态常量:
public static final int MIN_PRIORITY=1;最低优先级
public static final int MAX_PRIORITY=10;最高优先级
public static final int NORM_PRIORITY=5;默认优先级
通过setPriority(int newPriority)可以设置线程的优先级,优先级的范围是1-10,默认的情况下,线程的优先级是5,。在上网看了其他用户的博客和自己的实践,线程优先级的设置主要的是设置的是处理器调度线程的概率的高低而已,即线程的优先级越高,处理器调度的几率越大,并不意味着线程优先级越高就一定会先执行,也可能是先执行优先级较低的线程。
2.sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
3.wait(long millis)根据官方文档,在其他线程调用notify()或者notifyAll()方法前,线程处于等待状态。wait()其实就是wait(0L)。调用wait(long millis)的线程必须拥有此对象监视器,该线程发布监视器所有权。这里的对象监视器通常是调用同步方法的对象,监视器的所有权就是得到进入同步方法的锁。也就是说wait(long millis)必须在同步方法里面,当wait方法被调用的时候,线程才能将锁的权限让出去给其他线程竞争,直到其他拥有对象监视器的线程调用notify()或notifyAll()才能唤醒该线程,然后该线程重新与其他阻塞的线程一起竞争锁的权限。
4.notify()
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait
方法,在对象的监视器上等待。直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者:
- 通过执行此对象的同步实例方法。
- 通过执行在此对象上进行同步的
synchronized
语句的正文。 - 对于
Class
类型的对象,可以通过执行该类的同步静态方法。
一次只能有一个线程拥有对象的监视器
5.join();
等待该线程的终止。 其实就是在其他线程(例如Thread1)中,调用当前线程(Thread2)的join()方法,则Thread1会等待Thread2执行完后才会执行Thread1线程。join(long millis)就是Thread1等待Thread2执行具体的毫秒数后才执行Thread1。
6.yield();暂停当前正在执行的线程对象,并执行其他线程。 其实这个方法的解释也是比较有歧义的。实践证明,启动两个线程(Thread1和Thread2),当一个Thread1调用yield方法的时候,Thread2并不一定会被处理器调度执行,也可能还是Thread1先执行。也就是说Thread1调用yield方法,其实是将处理器资源让出去,然后与其他线程一起重新竞争获取处理器资源。
线程中断
线程中断常用的方法:
isAlive();判断线程是否已经终止
interrupe();中断线程 如果线程在调用 Obeject类的 wait()
、wait(long)
或 wait(long,int)
方法,或者该类的 join()
、join(long)、join(long,int)、sleep()或 sleep(long,int) 方法过程中受阻,则其中断状态将被清除,它还将跑出InterrupeExceptioin异常。
interruped();测试线程是否已经中断,线程的中断状态由该方法清除。如果连续调用两次,那么第二次返回false。
isInterruped();测试线程是否已经中断,线程的中断状态不收该方法的影响。
通过interrupt方法给线程设置中断状态,线程不会立即终止,系统会在适当的时候,终止线程。
线程的同步机制
在平时开发的过程中,多线程的身影总是十分常见的。多线程的使用,如果不采取任何其他的措施,那么就会引发一些问题。譬如多个线程同时对一个实体类对象进行set()get(),那么很大的概率上得到的数据是脏数据,即不是原来预期的数据,因为线程是由处理器所调度的,不受用户所控制。如果我们要得到正确的数据,那么使用线程的同步机制就可以解决问题了。
多线程同时对同一个资源进行操作,线程由处理器进行调度,彼此之间没有联系,线程之间存在着竞争的关系。对于线程间的竞争关系,需要实现线程的互斥。
Java提供关键字synchronized用于声明一段程序为临界区,是线程对临界资源采用互斥方式。synchronized有两种方式:声明一条语句,或者声明一个方法。
(1)同步语句 使用synchronized声明一条语句为临界区,该语句成为同步语句: synchronized (对象) 语句 其中,<对象>是多个线程的公共变量,即是需要被锁定的临界资源,它将被互斥的使用;<语句>是临界区,描述线程对临界资源的操作。
(2)同步方法 使用synchronized声明一个方法,该方法称为同步方法,同步方法的方法体为临界区,互斥使用(锁定)的是调用该方法的对象。synchronized (this){方法体}
java程序设计的经典案例:
public class TestThread extends Thread{
public static void main(String[] args) {
Account account = new Account();
new SaveThread(account,100).start();
new SaveThread(account,200).start();
new FetchThread(account,300).start();
}
}
class FetchThread extends Thread{
private Account account;
private int value;
public FetchThread(Account account,int value) {
this.account = account;
this.value = value;
}
@Override
public void run() {
synchronized(this.account) {//声明临界区,锁定指定账号对象
System.out.println("取出:"+account.fetch(value)+"余额:"+account.getBalance());
}
}
}
class SaveThread extends Thread{
private Account account;
private int value;
public SaveThread(Account account,int value) {
this.account = account;
this.value = value;
}
@Override
public void run() {
synchronized(this.account) {//声明临界区,锁定指定账号对象
account.save(value);
System.out.println("存入:"+value+"余额:"+account.getBalance());
}
}
}
class Account{
private String name;
private int balance;
public void setBalance(int balance) {
this.balance = balance;
}
public int getBalance() {
return this.balance;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void save(int value) {
if(value > 0)
balance += value;
}
public int fetch(int value) {
if(value <= 0) {
return 0;
}
if(value <= balance) {
balance -= value;
}else {
value = balance;
balance = 0;
}
return value;
}
}
线程的协作
java程序设计经典案例
public class MultiThread {
public static void main(String[] args) {
Buffer buffer = new Buffer();
SendThread send = new SendThread(buffer);
ReceiveThread receive = new ReceiveThread(buffer);
send.start();
receive.start();
}
}
class Buffer{
private int value;
private static boolean ISEMPTY = true;
public synchronized void put(int value) {
while(!ISEMPTY) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("value:"+value);
this.value = value;
ISEMPTY = false;
this.notify();
}
public synchronized int get() {
while(ISEMPTY) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
ISEMPTY = true;
this.notify();
return this.value;
}
}
class SendThread extends Thread{
private Buffer buffer;
public SendThread(Buffer buffer){
this.buffer = buffer;
}
@Override
public void run() {
for(int i= 1;i < 10;i++) {
buffer.put(i);
System.out.println(this.getClass().getSimpleName()+" put:"+i);
}
}
}
class ReceiveThread extends Thread{
private Buffer buffer;
public ReceiveThread(Buffer buffer){
this.buffer = buffer;
}
@Override
public void run() {
for(int i= 1;i < 10;i++) {
buffer.get();
System.out.println(this.getClass().getSimpleName()+" get:"+this.value);
}
}
}