——- android培训、java培训、期待与您交流! ———-
黑马程序员——多线程
创建线程方式一
- 继承Thread类
- 子类覆盖父类中的run方法,将线程运行的代码存放在run中。
- 建立子类对象的同时线程也被创建。
- 通过调用start方法开启线程。
/*
创建线程方式一:继承Thread类。
步骤:
1,定义一个类继承Thread类。
2,覆盖Thread类中的run方法。
3,直接创建Thread的子类对象创建线程。
4,调用start方法开启线程并调用线程的任务run方法执行。
可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)
主线程的名字就是main。
*/
class Demo extends Thread {
private String name;
Demo(String name) {
super(name);
// this.name = name;
}
public void run() {
for (int x = 0; x < 10; x++) {
// for(int y=-9999999; y<999999999; y++){}
System.out.println(name + "....x=" + x + ".....name="
+ Thread.currentThread().getName());
}
}
}
class ThreadDemo1 {
public static void main(String[] args) {
/*
* 创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。
*
* 而运行的指定代码就是这个执行路径的任务。
*
* jvm创建的主线程的任务都定义在了主函数中。
*
* 而自定义的线程它的任务在哪儿呢? Thread类用于描述线程,线程是需要任务的。所以Thread类也对任务的描述。
* 这个任务就通过Thread类中的run方法来体现。也就是说,run方法就是封装自定义线程运行任务的函数。
*
* run方法中定义就是线程要运行的任务代码。
*
* 开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法。 将运行的代码定义在run方法中即可。
*/
// Thread t1 = new Thread();
Demo d1 = new Demo("旺财");
Demo d2 = new Demo("xiaoqiang");
d1.start();// 开启线程,调用run方法。
d2.start();
System.out.println("over...." + Thread.currentThread().getName());
}
}
线程的四种状态
sleep方法需要指定睡眠时间,单位是毫秒。
一个特殊的状态:就绪。具备了执行资格,但是还没有获取资源。
创建线程方式二
- 实现Runnable接口
- 子类覆盖接口中的run方法。
- 通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。
- Thread类对象调用start方法开启线程。
/*
创建线程的第一种方式:继承Thread类。
创建线程的第二种方式:实现Runnable接口。
1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
所以要在线程对象创建时就必须明确要运行的任务。
4,调用线程对象的start方法开启线程。
实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。
所以,创建线程的第二种方式较为常用。
*/
class Demo implements Runnable// 准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。// 通过接口的形式完成。
{
public void run() {
show();
}
public void show() {
for (int x = 0; x < 20; x++) {
System.out.println(Thread.currentThread().getName() + "....." + x);
}
}
}
class ThreadDemo2 {
public static void main(String[] args) {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
// Demo d1 = new Demo();
// Demo d2 = new Demo();
// d1.start();
// d2.start();
}
}
线程安全问题
- 导致安全问题的出现的原因:
- 多个线程访问出现延迟。
- 线程随机性 。
- 注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
同步(synchronized)
格式:
synchronized(对象) { 需要同步的代码; }
同步可以解决安全问题的根本原因就在那个对象上。
该对象如同锁的功能。
- 同步的特点
- 同步的前提:
- 同步需要两个或者两个以上的线程。
- 多个线程使用的是同一个锁。
- 未满足这两个条件,不能称其为同步。
- 同步的弊端:
- 当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
- 同步的前提:
- 同步函数
- 格式:
在函数上加上synchronized修饰符即可。
- 格式:
线程间通信
- 思考1:wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?
- 这些方法存在与同步中。
- 使用这些方法时必须要标识所属的同步的锁。
- 锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
- 思考2:wait(),sleep()有什么区别?
- wait():释放cpu执行权,释放锁。
- sleep():释放cpu执行权,不释放锁。
停止线程
- 定义循环结束标记
- 因为线程运行代码一般都是循环,只要控制了循环即可。
- 使用interrupt(中断)方法。
- 该方法是结束线程的冻结状态,使线程回到运行状态中来。
注:stop方法已经过时不再使用。
经典案例:生产者消费者
/*
生产者,消费者。
多生产者,多消费者的问题。
if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行!
notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。
*/
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
public synchronized void set(String name)//
{
while (flag)
try {
this.wait();
} catch (InterruptedException e) {
}// t1 t0
this.name = name + count;// 烤鸭1 烤鸭2 烤鸭3
count++;// 2 3 4
System.out.println(Thread.currentThread().getName() + "...生产者..."
+ this.name);// 生产烤鸭1 生产烤鸭2 生产烤鸭3
flag = true;
notifyAll();
}
public synchronized void out()// t3
{
while (!flag)
try {
this.wait();
} catch (InterruptedException e) {
} // t2 t3
System.out.println(Thread.currentThread().getName() + "...消费者........"
+ this.name);// 消费烤鸭1
flag = false;
notifyAll();
}
}
class Producer implements Runnable {
private Resource r;
Producer(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.set("烤鸭");
}
}
}
class Consumer implements Runnable {
private Resource r;
Consumer(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.out();
}
}
}
class ProducerConsumerDemo {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t0 = new Thread(pro);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
Thread t3 = new Thread(con);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
使用Lock接口:
/*
jdk1.5以后将同步和锁封装成了对象。
并将操作锁的隐式方式定义到了该对象中,
将隐式动作变成了显示动作。
Lock接口: 出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成显示锁操作。
同时更为灵活。可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,通常需要定义finally代码块中。
Condition接口:出现替代了Object中的wait notify notifyAll方法。
将这些监视器方法单独进行了封装,变成Condition监视器对象。
可以任意锁进行组合。
await();
signal();
signalAll();
*/
import java.util.concurrent.locks.*;
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
// 创建一个锁对象。
Lock lock = new ReentrantLock();
// 通过已有的锁获取该锁上的监视器对象。
// Condition con = lock.newCondition();
// 通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
Condition producer_con = lock.newCondition();
Condition consumer_con = lock.newCondition();
public void set(String name)// t0 t1
{
lock.lock();
try {
while (flag)
// try{lock.wait();}catch(InterruptedException e){}// t1 t0
try {
producer_con.await();
} catch (InterruptedException e) {
}// t1 t0
this.name = name + count;// 烤鸭1 烤鸭2 烤鸭3
count++;// 2 3 4
System.out.println(Thread.currentThread().getName()
+ "...生产者5.0..." + this.name);// 生产烤鸭1 生产烤鸭2 生产烤鸭3
flag = true;
// notifyAll();
// con.signalAll();
consumer_con.signal();
} finally {
lock.unlock();
}
}
public void out()// t2 t3
{
lock.lock();
try {
while (!flag)
// try{this.wait();}catch(InterruptedException e){} //t2 t3
try {
cousumer_con.await();
} catch (InterruptedException e) {
} // t2 t3
System.out.println(Thread.currentThread().getName()
+ "...消费者.5.0......." + this.name);// 消费烤鸭1
flag = false;
// notifyAll();
// con.signalAll();
producer_con.signal();
} finally {
lock.unlock();
}
}
}
class Producer implements Runnable {
private Resource r;
Producer(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.set("烤鸭");
}
}
}
class Consumer implements Runnable {
private Resource r;
Consumer(Resource r) {
this.r = r;
}
public void run() {
while (true) {
r.out();
}
}
}
class ProducerConsumerDemo2 {
public static void main(String[] args) {
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t0 = new Thread(pro);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
Thread t3 = new Thread(con);
t0.start();
t1.start();
t2.start();
t3.start();
}
}