----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
Blog_3_1 多线程基础概念
为了可以让不同的程序块一起运行,让程序运行更为顺畅,同时也可达到多任务处理的目的,java引入了多线程的概念。
进程: 进程是程序的一次动态执行过程, 它经历了从代码加载、执行到执行完毕的一个完整过程,这个过程也是进程本身从产生、发展到最终消亡的过程。
线程:程序内部的代码执行路径,一个进程可以有多条执行路径。
Blog_3_2 实现多线程的两种方法
Java中提供了Thread类和Runnable接口,来实现多线程,下面分别用Thread和Runnable来实现多线程。
(1) 通过继承Thread类来实现多线程
通过继承Thread类来实现多线程的步骤有二步如下:
(a).编写一个类并继承Thread类并重写run方法,其中run方法中的代码就是多线程运算的代码块,如下:
public classMyThread extendsThread {
@Override
public void run() {
for (int x = 0; x < 1000;x++) {
System.out.println(x);
}
}
}
(b).创建MyThread类的对象并调用start()方法开启多线程,如下
MyThread my1 =new MyThread();//创建了MyThread对象
MyThread my2 =new MyThread();
my1.start();//开启多线程
my2.start();
(2) 通过Runnable接口实现多线程
(a) 自定义类MyRunnable实现Runnable接口并重写run()方法
public classMyRunnable implementsRunnable {
@Override
public void run() {
for (int x = 0; x < 100; x++){
System.out.println(Thread.currentThread().getName()+"---"+ x);
}
}
}
(b) 创建MyRunnable类的对象
MyRunnablemy = new MyRunnable();
(c) 创建Thread类的对象,把MyRunnable类的对象作为构造参数传递
Thread t1 = new Thread(my, "线程1");
Thread t2 = new Thread(my, "线程2");
(d) 调用start()方法开启多线程
t1.start();
t2.start();
(3) 两种机制的的特点及比较
A、适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码、数据有效分离,较好地体现了面向对象的设计思想。
B、可以避免由于Java的单继承特性带来的局限。开发中经常碰到这样一种情况,即:当要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么就只能采用实现Runnable接口的方式了。
C、增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程可以操作相同的数据,与它们的代码无关。当共享访问相同的对象时,即共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。
Blog_3_3 多线程的同步
(1) 多线程不同步的原因分析
通过一个买票的实例来分析出现多线程不同步的原因:
书写一个Ticket类实现Runnable接口
public class Ticket implements Runnable {
private int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() +"正在出售第"+tickets + "张票");
ticket--;
}
}
}
}
Ticket的测试类:
public class TicketDemo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread t1 = new Thread(t,"窗口1");
Thread t2 = new Thread(t,"窗口2");
Thread t3 = new Thread(t,"窗口3");
Thread t4 = new Thread(t,"窗口4")
t1.start();
t2.start();
t3.start();
t4.start();
}
}
上面程序打印出来的结果是:
……..
窗口1正在出售第2张票
窗口4正在出售第1张票
窗口1正在出售第0张票
窗口2正在出售第-1张票
窗口3正在出售第-2张票
发现打印结果出现了0,-1,-2的不符合常规的结果,是怎样产生这样的结果呢,下面来分析:
当ticket只剩1张时:
假设“窗口1”首先抢到了cpu的执行权,当执行到ticket--,前一步时被“窗口2”抢到了执行权,此时ticket值为1,“窗口2”也能进入if (tickets > 0)内部,当“窗口1”再次抢到cpu执行权时,打印出来的即是窗口1正在出售第0张票,当“窗口2”接着运行打印代码时结果是窗口2正在出售第-1张票。同理,窗口3也能打印出窗口3正在出售第-2张票。综上所述产生线程不同步的条件有三个:
A:多线程程序(存在在同一时间抢夺cup执行权的情况)
B:有共享数据
C:共享数据被多条语句操作
(2) 两种同步线程的方法
①使用synchronized为存在线程安全问题的代码“上锁”。
如上面的代码可以这样处理使其同步:
public class Ticket implements Runnable {
private int tickets = 100;
Object obj=new Object();
@Override
public void run() {
while (true) {
synchronized(obj){
/*使用synchronized关键字为存在线程安全问题的代码上锁,括号内的对象一样,则代表锁是同一把。如果不同线程调用run方法时,此处的obj不是同一个对象的话,则无法使线程同步*/
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() +"正在出售第"+tickets + "张票");
ticket--;
}
}
}
}
}
②在方法声明部分使用 synchronized关键字,使不安全的线程同步,如
public class Ticket implements Runnable {
private int tickets = 100;
@Override
Public synchronized void run() {
while (true) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() +"正在出售第"+ (tickets--) +"张票");
}
}
}
}
在方法声明上加了synchronized关键字,同样可以达到使有线程安全的代码同步。
(3) 一个死锁的代码
一旦有多个进程,且它们都要争用对多个锁的独占访问,那么就有可能发生死锁。如果有一组进程或线程,其中每个都在等待一个只有其它进程或线程才可以执行的操作,那么就称它们被死锁了,如下面代码:
public class MyLock {
public static final Object lockA =new Object();
public static final Object lockB =new Object();
}
public class DieLock extends Thread {
private boolean flag;
public DieLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
while (true) {
synchronized (MyLock.lockA) {
System.out.println("iflockA");// t1的执行权没有了
synchronized (MyLock.lockB) {
System.out.println("iflockB");
}
}
}
} else {
while (true) {
synchronized (MyLock.lockB) {
System.out.println("elselockB");// t2的执行权没有了
synchronized (MyLock.lockA) {
System.out.println("elselockA");
}
}
}
}
}
}
public class DieLockDemo {
public static void main(String[] args) {
DieLock dl1 = new DieLock(true);
DieLock dl2 = new DieLock(false);
dl1.start();
dl2.start();
}
}
Blog_3_4 多线程间的通讯
Java是通过Object类的wait、notify、notifyAll这几个方法来实现线程间的通信的,又因为所有的类都是从Object继承的,所以任何类都可以直接使用这些方法。
wait:告诉当前线程放弃监视器并进入睡眠状态,直到其它线程进入同一监视器并调用notify为止。
notify:唤醒同一对象监视器中调用wait的第一个线程。类似排队买票,一个人买完之后,后面的人可以继续买。
notifyAll:唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行。
----------------------- android培训、java培训、java学习型技术博客、期待与您交流! ----------------------
详情请查看: