您的位置:首页 > 职场人生

黑马程序员——Java基础——多线程(二)

2015-07-16 11:16 585 查看
------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

一、死锁

由于线程可以获取多个锁,比如在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁,这样就可能会出现同步嵌套的情况。当两个线程被阻塞,每个线程在等待另一个线程时会导致死锁。在开发中一定要避免出现死锁。

class DeadLock implements Runnable
{
//定义一个线程运行的标记
private boolean flag;
DeadLock(boolean flag)
{
this.flag = flag;
}
//定义两个对象锁
static Object locka = new Object();
static Object lockb = new Object();

//定义线程运行中的同步嵌套
public void run()
{
if(flag)
{
synchronized(locka)
{
System.out.println(Thread.currentThread().getName()+"...if locka ");
synchronized(lockb)
{
System.out.println(Thread.currentThread().getName()+"..if lockb");
}
}

}
else
{
synchronized(lockb)
{
System.out.println(Thread.currentThread().getName()+"..else lockb");
synchronized(locka)
{
System.out.println(Thread.currentThread().getName()+".....else locka");
}
}
}
}
}
class DeadLockDemo
{
public static void main(String[] args)
{
//定义两个线程,在构造中传递不同的标记并运行
Thread t1 = new Thread(new DeadLock(true));
Thread t2 = new Thread(new DeadLock(false));
t1.start();
t2.start();
}
}


二、线程间的通信

1.线程间的通信就是多个线程在操作同一个资源,但是操作的动作却不一样,比如多线程常见的生产者—消费者-仓库模式,实现存入仓库一个资源,接着取出一个。

//创建仓库资源
class Resource {
private String name;
private int count = 1;
private boolean flag = false;
//定义存和取资源的方法,因为属于多个线程共同操作的内容,所以要加同步
public synchronized void setIn(String name) {
//根据定义的标记判断仓库内是否有资源
//没有就往仓库存放,存放结束后改变标记,唤醒等待的线程
//有就线程等待取出结束
while (flag)
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "...生产者.."
+ this.name);
flag = true;
this.notifyAll();
}
public synchronized void takeOut() {
//根据定义的标记判断仓库内是否有资源
//有就从仓库取出,取出结束后改变标记,唤醒等待的线程
//如果没有就线程等待存放结束
while (!flag)
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "...消费者.."
+ this.name);
flag = false;
this.notifyAll();
}
}
//定义生产者,存放商品,循环十次方便查看结果
class Producer implements Runnable {
private Resource res;

Producer(Resource res) {
this.res = res;
}

public void run() {
for(int x = 0 ; x < 10 ; x++)
{
res.setIn("+商品+");
}
}
}
//定义消费者,取出商品
class Consumer implements Runnable {
private Resource res;

Consumer(Resource res) {
this.res = res;
}

public void run() {
for(int x = 0; x < 10; x++)
{
res.takeOut();
}
}
}

//创建线程分别进行存放和读取,并运行
public class ProducerConsumerDemo {
public static void main(String[] args) {
Resource r = new Resource();

Producer pro = new Producer(r);
Consumer con = new Consumer(r);

Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);

t1.start();
t2.start();
t3.start();
t4.start();

}
}




2.线程状态及常用方法

1)线程状态

被创建:线程对象已经创建,还没有在其上调用start方法。

可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。
运行状态:获取了CPU执行权,执行程序代码。

临时状态(阻塞):有执行资格,但是没有执行权。
冻结状态:遇到sleep方法和wait方法时,失去执行资格和执行权,sleep方法时间到或者调用notify方法时,获得执行资格,变为临时状态。
消忙状态:线程执行完了或者因异常退出了run方法,该线程结束生命周期
2)常用方法

wait:将同步中的线程处于冻结状态。释放了CPU执行权,释放了锁。同时将线程对象存储到线程池中。

sleep:释放CPU执行权,不释放锁。

notify:唤醒线程池中某一个等待线程。

notifyAll:唤醒的是线程池中的所有线程。

3.JDK1.5的升级

JDK1.5 中直接将锁封装成对象,把同步Synchronized替换成显示的Lock操作。将Object中的wait,notify notifyAll,替换了Condition对象。该对象可以通过Lock锁进行获取,并支持多个相关的Condition对象,可以针对性的唤醒指定Condition对象的线程。生产者消费者代码也可以用下面的代码实现:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//创建仓库资源
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;

private Lock lock = new ReentrantLock();
//创建两个Condition,控制生产者和消费者线程间的通信
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();

public void setIn(String name) throws InterruptedException
{
//获取锁
lock.lock();
try
{
//根据定义的标记判断仓库内是否有资源
//没有就往仓库存放,存放结束后改变标记,唤醒消费者等待的线程
//有就等待消费者线程取出结束
while(flag)
condition_pro.await();
this.name = name+"--"+count++;

System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
condition_con.signal();
}
//释放锁的动作一定要执行,所以使用finally
finally
{
lock.unlock();
}
}
public void takeOut() throws InterruptedException
{
//获取锁
lock.lock();
try
{
//根据定义的标记判断仓库内是否有资源
//有就从仓库取出,取出结束后改变标记,唤醒生产者等待的线程
//如果没有就等待生产者线程存放结束
while(!flag)
try {
condition_con.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
flag = false;
condition_pro.signal();
}
//释放锁的动作一定要执行,所以使用finally
finally
{
lock.unlock();
}

}
}
//定义生产者,存放商品,循环十次方便查看结果
class Producer implements Runnable
{
private Resource res;

Producer(Resource res)
{
this.res = res;
}
public void run()
{
for(int x = 0 ;x < 10; x++)
{
try {
res.setIn("+商品+");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//定义消费者,取出商品
class Consumer implements Runnable
{
private Resource res;

Consumer(Resource res)
{
this.res = res;
}
public void run()
{
for(int x = 0 ;x < 10; x++)
{
try {
res.takeOut();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}
}

//创建线程分别进行存放和读取,并运行
public class ProducerConsumerDemo {
public static void main(String[] args) {
Resource r = new Resource();

Producer pro = new Producer(r);
Consumer con = new Consumer(r);

Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);

t1.start();
t2.start();
t3.start();
t4.start();

}
}


三、多线程题目

1.下列程序的运行结果

new Thread(new Runnable(){
public void run()
{
System.out.println("接口线程...");
}
})
{
public void run()
{
System.out.println("类线程...")
a52d
;
}
}.start();

运行结果:



这个题目使用两个匿名内部类,在没指定的情况下,子类构造函数调用的都是父类无参的构造函数,所以传入实现Runnbale的子类对象没有作用。

2.创建3个线程依次打印ABC,并且循环10次

public class ABCThread {

// 定义标记,控制打印顺序和打印次数
static int count = 1;

public static void main(String[] args) {

// 创建同步代码块的锁
Object obj = new Object();

//因为只有简单的多线程运行代码,所以使用匿名内部类传递
Thread A = new Thread(new Runnable() {
@Override
//复写run方法,判断标记,如果符合就等待,
//如果不符合执行打印代码,并使标记加1,唤醒其他所有在等待的线程
public void run() {
while (count <= 30) {
synchronized (obj) {
if (count % 3 != 1)
try {
obj.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
else {
System.out.println("A");
count++;
obj.notifyAll();
}
}

}
}
});
Thread B = new Thread(new Runnable() {
@Override
//复写run方法,判断标记,如果符合就等待,
//如果不符合执行打印代码,并使标记加1,唤醒其他所有在等待的线程
public void run() {
while (count <= 30) {
synchronized (obj) {
if (count % 3 != 2)
try {
obj.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
else {
System.out.println("B");
count++;
obj.notifyAll();

}

}
}
}
});
Thread C = new Thread(new Runnable() {
@Override
//复写run方法,判断标记,如果符合就等待,
//如果不符合执行打印代码,并使标记加1,唤醒其他所有在等待的线程
public void run() {
while (count <= 30) {
synchronized (obj) {
if (count % 3 != 0)
try {
obj.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
else {
System.out.println("C");
System.out.println("-------"+count/3);
count++;
obj.notifyAll();
}

}
}
}
});
A.start();
B.start();
C.start();
}
}







内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: