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

黑马程序员——Java基础之多线程

2015-07-02 09:16 726 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一、线程简介

要先了解线程就得知道进程。进程就是正在执行的程序,也就是程序执行的路径。线程就是进程中的独立控制单元,线程控制着进程的执行,一进程至少有一个线程,当有多个线程时,每个线程完成一个功能,并与其他线程并发执行,这种机制就叫多线程。当JVM启动除了执行一个主线程,还有负责垃圾回收机制的线程。

多线程的意义:让程序同时运行,提高程序执行效率。

windows操作系统CPU的工作原理:系统可以分配给每个进程一段有限的CPU时间片,CPU在这段时间中执行某个进程,然后下一个时间片又跳到另一个进程中去执行。由于CPU这样的跳转很快,所以使得每个进程好像是同时执行一样。

二、实现线程的两种方式:

1、继承Thread类

通过继承Thread类,覆盖类中的run()方法,通过Thread类中的start()方法来执行线程。

创建步骤:1).定义一个类继承Thread类。2.)覆盖Thread类中的run方法。3).直接创建Thread的子类对象创建线程。4).调用start方法开启线程并调用线程的任务run方法执行。

Thread类用于描述线程。该类就定义了一个功能,用于存储线程要执行的代码。该存储功能就run方法。也就是说,Thread类中的run方法,用于存储线程要运行的代码。

示例:

package com.heima.thread;

public class ThreadDemo extends Thread{

/**
* @param args
*/

private int x=10;
public void run()
{
while(x>=0)
{
System.out.println(x--+"  "+Thread.currentThread().getName());
}

}

public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadDemo a1=new ThreadDemo();
ThreadDemo a2=new ThreadDemo();
a1.start();
a2.start();
}

}


运行结果:

10 Thread-0

9 Thread-0

10 Thread-1

8 Thread-0

9 Thread-1

7 Thread-0

8 Thread-1

6 Thread-0

7 Thread-1

5 Thread-0

4 Thread-0

3 Thread-0

2 Thread-0

1 Thread-0

0 Thread-0

6 Thread-1

5 Thread-1

4 Thread-1

3 Thread-1

2 Thread-1

1 Thread-1

0 Thread-1

可见一个线程并非一次性执行完的。

2、实现Rannable接口

当我们已经继承了别的类但是又要实现多线程那怎么办,这时就可以用通过实现Runnable接口来实现。

创建步骤:

1).定义类实现Runnable接口。

2).覆盖接口中的run方法,将线程的任务代码封装到run方法中。

3).通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传

递。为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务。

 4).调用线程对象的start方法开启线程。

实现Rannable接口的好处就是打破单继承的局限性。所以当创建多线程的时候应该用这种实现Rannable接口方式。

示例:

package com.heima.thread;

public class ThreadDemo1 implements Runnable{

public void run()
{
show();
}

private void show() {
// TODO Auto-generated method stub
int x=0;
while(x<=10)
{
System.out.println(x+++"  "+Thread.currentThread().getName());
}
}

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadDemo1 a=new ThreadDemo1();
Thread t1=new Thread(a);
Thread t2=new Thread(a);
t1.start();
t2.start();

}

}


运行结果是:



如果start方法调用一个已经启动的线程,系统会抛出IllegalThreadStateException异常。

三、线程安全问题

先看一个例子:

package com.heima.thread;
/**
* 需求:简单的卖票程序,模拟4个线程同时卖100张票。多窗口同时卖票
* @author Administrator
*
*/
class Ticket implements Runnable{
private int ticket=100;
public void run()
{
while(true)
{
if(ticket>0)
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"…………"+ticket--);
}
}
}
}
public class ThreadTicket {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();

}

}


运行结果 :



通过结果可以看出 :买票居然卖出了负数,这是不可能的,这时多线程出现的安全问题,并且此时的CPU 利用率很高,那么为什么会出现在这种情况呢?

当多条语句在操作线程共享数据时,一个线程的语句只执行了一部分,这是另一个进程进来的,导致共享数据的错误。

解决办法是对操作共享数据的语句只让一个线程都执行完,而其他线程不能执行。这就需要

1)同步代码块,用关键字synchronized。

synchronized(对象)

{

需要被同步的代码

}

2)同步函数,格式:在函数上加上synchronized修饰符即可。它实用的锁是this。静态函数使用的锁是该类的字节码文件,即类.Class

上示例修改后的代码:

package com.heima.thread;
/**
* 需求:简单的卖票程序,模拟4个线程同时卖100张票。多窗口同时卖票
* @author Administrator
*
*/
class Ticket implements Runnable{
private int ticket=100;
Object obj=new Object();
public void run()
{
while(true)
{
synchronized (obj) {
if(ticket>0)
{
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"…………"+ticket--);
}
}

}
}
}
public class ThreadTicket {

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();

}

}


运行结果:



同步的好处:解决了线程的安全问题。

 同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,CPU利用率高无形中会降低程序的运行效率。

同步的前提:必须有多个线程并使用同一个锁。

如何找出多线程中的安全问题:

1、明确那些代码是多线程代码

2、明确共享数据

3、明确多线程运行代码中哪些语句是操作共享数据的。

多线程之懒汉式单例模式:实例延迟加载,多线程访问时会有安全问题,加同步能解决,锁是该类的字节码文件。

示例:

<span style="white-space:pre">	</span>private static Single getInstance()
{
if(s==null)
{
synchronized (Single.class) {
if (s==null) {
s=new Single();
}

}
}
return s;
}


四、线程死锁问题

什么叫死锁:是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作

用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进锁。

示例:

package com.heima.thread;
/**
* 写一个死锁程序
* @author Administrator
*
*/
class Test implements Runnable{
private boolean flag;
Test(boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{
synchronized (MyLock.aObject)
{
System.out.println("我是if      aObject");
synchronized (MyLock.bObject)
{
System.out.println("我是if      bObject");
}
}

}
else {
synchronized (MyLock.bObject)
{
System.out.println("我是else      bObject");
synchronized (MyLock.aObject)
{
System.out.println("我是else      aObject");
}
}

}
}
}
class MyLock{
static Object aObject=new Object();
static Object bObject=new Object();
}
public class DeadLock {

public static void main(String[] args) {
// TODO Auto-generated method stub
Thread aThread=new Thread(new Test(true));
Thread bThread=new Thread(new Test(false));
aThread.start();
bThread.start();

}

}


运行结果:



五、线程间通信

多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。

等待/唤醒机制涉及的方法:

1). wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。

2). notify():唤醒线程池中的一个线程(任何一个都有可能)。

3). notifyAll():唤醒线程池中的所有线程。

1、这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。

2、必须要明确到底操作的是哪个锁上的线程!

3、wait和sleep区别?

1)wait可以指定时间也可以不指定。sleep必须指定时间。

2)在同步中时,对CPU的执行权和锁的处理不同。

wait:释放执行权,释放锁。

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

为什么操作线程的方法wait、notify、notifyAll定义在了object类中,因为这些方法是监视器的方法, 监视器其实就是锁。锁可以是任意的对象,任意的对象调用的方式一定在object类中。

2、JDK1.5中提供了多线程升级解决方案。

将同步synchronized替换成显示的Lock操作。将Object中wait,notify,notifyAll,替换成了Condition对象。该Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。

下面是典型的生产者与消费者的问题

代码示例如下:

package com.heima.thread;

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

import javax.swing.LookAndFeel;

/**
* 生产者与消费者问题
* @author Administrator
*
*/
class Resource2{
private String name;
private int count=1;
private boolean flag=false;

private Lock look=new ReentrantLock();
private Condition condition_p=look.newCondition();
private Condition condition_c=look.newCondition();
public void set(String name) throws Exception
{
look.lock();
try {
while(flag)
{
condition_p.await();
}

this.name=name+"------"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
flag=true;
condition_c.signal();

}
finally{
look.unlock();
}
}
public void out() throws Exception
{
look.lock();
try{

while(!flag)
{
condition_c.await();
}
System.out.println(Thread.currentThread().getName()+"...消费者............"+this.name);
flag=false;
condition_p.signal();
}
finally{
look.unlock();
}
}

}
class Productor2 implements Runnable{
private Resource2 re;

public Productor2(Resource2 re) {
super();
this.re = re;
}

public void run()
{
while(true)
{
try {
re.set("++烤鸭+++");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class Consumer2 implements Runnable{
private Resource2 re;

public Consumer2(Resource2 re) {
super();
this.re = re;
}

public void run()
{
while(true)
{
try {
re.out();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class ProConDemo2 {
public static void main(String[] args) {
Resource2 r=new Resource2();
Productor2 pro=new Productor2(r);
Consumer2 con=new Consumer2(r);
Thread t1=new Thread(pro);
Thread t3=new Thread(pro);
Thread t2=new Thread(con);
Thread t4=new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();

}

}


运行结果为:

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