多线程

程序(program)

是为了完成特定任务,用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象

进程(process)

是程序的一次执行过程,或是正在运行的一个程序。动态过程:有它自身的产生,存在和消亡的过程
如运行中的qq,mp3播放器
程序是静态的,进程是动态的

线程(thread)

进程可进一步细化为线程,是一个程序内部的一条执行路径。
若一个程序可同时执行多个线程,就是支持多线程的

何时需要多线程

  • 程序需要同时执行两个或多个任务
  • 程序需要实现一些需要等待的任务时,如用户输入,文件读写操作。网络操作,搜索等
  • 需要一些后台进行的程序时

Thread

第一种方法(继承)

  1. 继承Java.lang.Thread
  2. 重写Thread的run()方法,方法内实现子线程要完成的功能
  3. 创建一个子类的对象
  4. 调用线程的start()方法。启动此线程;调用相应的run()方法
  5. Thread里的静态方法currentThread().getName():得到线程的名字
  6. 线程只能被执行一次start(),
  7. 不能通过实现类对象的run()方法去启动一个线程,就没启动新线程
常用方法
  1. start:启动线程,调用run方法
  2. run:子线程要执行的代码就放在这
  3. currentThread:Thread的静态方法,调取当前进程
  4. getName:获取线程的名字
  5. setName:设置此线程的名字
  6. yield:调用此方法的进程释放当前cpu的执行权
  7. join:在A线程中调用B线程的join方法,表示,当执行到此方法,A线程暂停,直至B线程执行完毕,A线程再接着执行
  8. isAlive:判断当前进程是否还存活,返回boolean
  9. sleep(long time):显式的让当前线程睡眠time,单位是毫秒
    10.stop:方法已过时,停止进程
  10. 线程通信:wait,notify,notifyAll,在Object里
  11. 设置线程的优先级:
  12. 线程的调度:
    时间片:按时间来
    抢占式:高优先级的线程抢占CPU
  13. Java的调度方法
    同优先级线程的组成先进先出的队列(先到先服务),使用时间片策略
    对高优先级,使用优先调度的抢占式策略
  14. 线程的优先级控制:
    MAX_PRIORITY(10)
    MIN_PRIORITY(1)
    NORM_PRIORTY(5):默认优先级
    涉及的方法:
    getPriority:获得线程的优先级值
    setPriority(int newPriority):设置新的优先级
    只是抢到cpu的概率变大了,并不是一定抢到
  • 继承实现多线程
class SubThread extends Thread {
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
	}
}

public class TestThread {
	public static void main(String[] args) {
		Thread s1 = new SubThread();
		s1.start();
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
	}
}

第二种方法(实现接口)

static修饰的属性被类的实例所共有!

  1. 通过实现Runnable接口,然后用想要实现的代码放在run方法里
  2. 要先启动一个多线程,必须调用start方法
  3. 创建一个实现Runnable接口实现类的对象,并将其传入Thread的构造方法
  4. Thread t1 = new Thread(new Test implements Runnable);
  5. public Thread(Runner target)
  6. start()调用的是target的run方法,也就是构造器里的形参
  • 实现接口来实现多线程
class Window1 implements Runnable {
	int ticket = 100;

	public void run() {
		while (true) {
			if (ticket > 0) {
				System.out.println(Thread.currentThread().getName() + "售出:" + ticket--);
			} else {
				break;
			}
		}
	}
}

public class TestWindowImplements {
	public static void main(String[] args) {
		Runnable ra = new Window1();
		Thread w1 = new Thread(ra);
		Thread w2 = new Thread(ra);
		Thread w3 = new Thread(ra);
		w1.setName("窗口一");
		w2.setName("窗口二");
		w3.setName("窗口三");//修改线程的名字
		w1.setPriority(10);//设定线程的优先级,使其有更高的概率抢到CPU的执行权
		w1.start();
		w2.start();
		w3.start();
	}
}

两种方法

  1. 联系:Thread实现了Runnable
  2. 实现Runnable的方法更加方便
    实现的方式避免Java里单继承的缺点
    如果多个线程要操作同一份资源(或数据),更适合实现的方式

多线程的好处

  1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。
  2. 提高计算机系统cpu的利用率
  3. 改善程序结构,将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

线程的分类

Java里的线程分为两类:一种是守护线程,一种是用户线程

  1. 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
  2. 守护线程是用来服务用户线程的,通过在start()方法前调用thread.setDaemon(true)可以把一个用户线程变成一个守护线程。
  3. Java垃圾回收就是一个典型的守护线程
  4. 若JVM中都是守护线程,当前JVM将退出

线程的生命周期

  1. 新建:
  2. 就绪:
  3. 阻塞:
  4. 运行:
  5. 死亡:
  6. 在这里插入图片描述

线程的同步机制

  1. 线程安全问题存在的原因?
    由于一个线程在操作共享数据的过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题
  2. 如何解决线程的安全问题?
    次必须让一个线程操作共享数据完毕以后,其它线程才能有机会参与共享数据的操作
  3. Java如何实现线程的安全:线程的同步机制
    方法一:同步代码块
    方法二:同步方法
同步代码块
synchronized(对象(同步监视器)){//对象在有的时候可以写this
	//需要被同步的代码(即为操作共享数据的代码)
}
  1. 共享数据:多个线程共同操作的同一个数据(变量)
  2. 同步监视器:由一个类的对象来充当。哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称,锁!
  3. 要求所有的线程必须用同一把锁
  4. 同步监器在一个进程访问共享数据时,会禁止其他进程访问。
  5. 注:在实现的方式中可以使用this来充当锁,若是继承的方式,就要慎用
class Window2 implements Runnable {
	int ticket = 100;
	public void run() {
		while (true) {
			synchronized (this) {//括号里填锁对象,任何类都可以,但在当前情况下必须要保证所有线程都是同一把锁
				if (ticket > 0) {
					try {
						Thread.currentThread().sleep(10);//睡眠10毫秒,可以使线程的不安全性大大的展示出来!
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "售出:" + ticket--);
				} else {
					break;
				}
			}
		}
	}
}

public class TestWindowImplements_code_block {
	public static void main(String[] args) {
		Runnable ra = new Window1();
		Thread w1 = new Thread(ra);
		Thread w2 = new Thread(ra);
		Thread w3 = new Thread(ra);
		w1.setName("窗口一");
		w2.setName("窗口二");
		w3.setName("窗口三");// 修改线程的名字
		w1.setPriority(10);// 设定线程的优先级,使其有更高的概率抢到CPU的执行权
		w1.start();
		w2.start();
		w3.start();
	}
}

同步方法

public synchronized void show():同步方法,能够保证当其中一个线程执行此方法时,其它线程在外等待直至此线程执行完成

  1. 同步方法的锁:this
  2. 同步代码块可以显式的指定锁,同步方法默认就为调用自己的对象
  3. 最好在实现方法的线程中使用
  • 案列:三个售票窗口售一百张票
class Window3 implements Runnable {
	int ticket = 100;

	public void run() {
		while(true) {
			show();
			if(ticket == 0) {
				break;
			}
		}
	}

	private  synchronized void show() {
//		while(true) {//while(true)写在这里会引发窗口一把所有数据都打印的结果
			try {
				Thread.currentThread().sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if (ticket > 0) {
				System.out.println(Thread.currentThread().getName() + "售出:" + ticket--);
			} else {
				return;
			}
//		}
	}
}

public class TestWindowImplements_code_method {
	public static void main(String[] args) {
		Runnable ra = new Window3();
		Thread w1 = new Thread(ra);
		Thread w2 = new Thread(ra);
		Thread w3 = new Thread(ra);
		w1.setName("窗口一");
		w2.setName("窗口二");
		w3.setName("窗口三");// 修改线程的名字
//		w1.setPriority(10);// 设定线程的优先级,使其有更高的概率抢到CPU的执行权
		w1.start();
		w2.start();
		w3.start();
	}
}

互斥锁—单例之懒汉式的线程安全

  1. 关于懒汉式的线程安全问题:使用同步机制
  2. 对于一般的方法内,使用同步代码块。可以考虑使用this
  3. 对于静态方法而言,使用当前类本身充当锁
  4. 线程的同步弊端:由于同一时间只能有一个线程访问共享数据,效率变低了
    此代码线程不安全
class Singleton {
	private Singleton() {
	}

	private static Singleton instance = null;

	public static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}

此代码线程安全

class Singleton {
	private Singleton() {
	}

	private static Singleton instance = null;

	public static Singleton getInstance() {
		synchronized (Singleton.class) {
			if (instance == null) {
				instance = new Singleton();
			}
		}
		return instance;
	}
}

改进的版本

class Singleton {
	private Singleton() {
	}

	private static Singleton instance = null;

	public static Singleton getInstance() {
		if(instance == null) {
			synchronized (Singleton.class) {
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

释放锁的操作

  1. 当前线程的同步方法,同步代码块执行结束
  2. 当前线程在同步代码块,同步方法中遇到break,return终止了代码,该方法的继续执行
  3. 当前线程在同步代码块,同步方法中出现了未处理的Error或Exception,导致异常结束
  4. 当前线程在同步代码块,同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁

不会释放锁的操作

  1. 线程执行同步代码块或同步方法时,程序调用Thread.sleep(),Thread.yield()方法暂停当前线程的执行
  2. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)
  3. 应尽量避免使用suspend()和resume()来控制线程
  • 实例,银行甲乙向账户各打三次钱
/**
 * 1.是否涉及多线程?是,有两个用户(两种方法创建多线程) 2.是否有共享数据?有,同一账户 3.得考虑线程得同步。(两种方法处理线程的安全)
 * 
 * @author Lenovo
 *
 */

class Account {
	double balance;

	public Account() {
	}

	public synchronized void deposit(double amt) {
		balance += amt;
		System.out.println(Thread.currentThread().getName() + ":余额:" + balance);
	}
}

class Customer extends Thread {
	Account account;

	public Customer(Account account) {
		this.account = account;
	}

	public void run() {
		for (int i = 0; i < 3; i++) {
			account.deposit(1000);
		}
	}
}

public class TestAccount {
	public static void main(String[] args) {
		Account account = new Account();
		Customer c1 = new Customer(account);
		Customer c2 = new Customer(account);
		c1.setName("甲");
		c2.setName("乙");
		c1.start();
		c2.start();
	}
}

死锁的问题

不同的线程分别暂用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成线程的死锁
解决方法:专门的算法,原则;尽量减少同步资源的定义

  • 案列:死锁
package demo;

public class TestDeadLock {
	static StringBuffer sb1 = new StringBuffer();
	static StringBuffer sb2 = new StringBuffer();

	public static void main(String[] args) {
		new Thread() {
			public void run() {
				synchronized (sb1) {
					try {
						Thread.currentThread().sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					sb1.append("A");
					synchronized (sb2) {
						sb2.append("B");
					}
				}
				System.out.println(sb1);
				System.out.println(sb2);
			}
		}.start();

		new Thread() {
			public void run() {
				synchronized (sb2) {
					sb1.append("C");
					synchronized (sb1) {
						sb2.append("D");
					}
					System.out.println(sb1);
					System.out.println(sb2);
				}
			}
		}.start();
	}
}

线程的通信

  1. wait
    会使当前线程挂起并放弃CPU,同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问
  2. notify
    唤醒正在排队等待的同步资源的线程中优先级最高者结束等待
  3. notifyAll
    唤醒正在排队等待资源的所有线程结束等待
  • Java.lang.Object提供的这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报Java.lang.IllegalMonitorStateException异常
  • 案列,使用两个线程打印1-100,线程一和线程二交替打印
class PrintNum implements Runnable {
	int num = 1;

	@Override
	public void run() {
		while (true) {
			synchronized (this) {
				if (num <= 100) {
					notify();
					try {
						Thread.currentThread().sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + ":" + num);
					num++;
					try {
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				} else {
					break;
				}
			}
		}
	}

}

public class TestCommunication {
	public static void main(String[] args) {
		PrintNum p = new PrintNum();
		Thread t1 = new Thread(p);
		Thread t2 = new Thread(p);
		t1.setName("甲");
		t2.setName("乙");
		t1.start();
		t2.start();
	}
}

  • 案列,甲乙交互打钱
/**
 * 1.是否涉及多线程?是,有两个用户(两种方法创建多线程) 2.是否有共享数据?有,同一账户 3.得考虑线程得同步。(两种方法处理线程的安全)
 * 
 * @author Lenovo
 *
 */

class Account {
	double balance;

	public Account() {
	}

	public synchronized void deposit(double amt) {
		notify();
		balance += amt;
		System.out.println(Thread.currentThread().getName() + ":余额:" + balance);
		try {
			wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

class Customer extends Thread {
	Account account;

	public Customer(Account account) {
		this.account = account;
	}

	public void run() {
		for (int i = 0; i < 10; i++) {
			account.deposit(1000);
		}
	}
}

public class TestAccount {
	public static void main(String[] args) {
		Account account = new Account();
		Customer c1 = new Customer(account);
		Customer c2 = new Customer(account);
		c1.setName("甲");
		c2.setName("乙");
		c1.start();
		c2.start();
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值