---------------------- <ahref="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<ahref="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------
概念:
Java是少数的几种支持多线程语言之一。大多数程序设计语言只能运行单独的一个程序块,无法同时运行不同的多个程序块,而Java中的多线程机制可以拟补这个缺憾,它可以让不同的程序块同时运行,这样可以让程序运行更为顺畅,性能更高,同时也可以达到多任务的处理目的。
谈起线程,现得了解进程概念,进程是一次动态的执行过程,他需经历代码加载、代码执行、以及代码执行完毕的过程。多进程的操作系统可同时运行多个进程(程序),例如:Windows操作系统就可在任务管理器中对进程进行查看(如下图),由于CPU具备分时机制,所以每个进程都可循环获得自己的CPU使用时间段,由于CPU执行速度非常快,使得好像所有进程在“同一时间“进行。
而多线程是实现并发机制的一种手段,线程是比进程还要小的执行单位,线程是在进程的基础上进行的进一步划分。所谓多线程是指一个进程在执行过程中可以产生更多的程序单元,这些更小单元称之为线程。
Java中实现线程:
在Java中要想实现多线程操作有两种方法:
一、继承Thread类
二、实现Runnable接口
继承Thread类:
Thread类是java.lang包中定义的,一个类只要继承了Thread类并覆写其中run()方法,此类就可以称之为多线程的实现类,例如以下代码:
package ClassPackage;
//继承Thread类实现多线程
class ThreadTestextends Thread{ //继承Thread类 多线程的实现类
privateint sum=10;
publicvoid run()
{
for(;sum>0;sum--)
System.out.println("sum="+sum);
}
}
publicclass Test28 {
publicstatic void main(String args[]){
//创建两个线程,并启动
ThreadTest t1=new ThreadTest();
ThreadTest t2=new ThreadTest();
t1.start();
t2.start();
}
}
运行结果:
从运行结果中我们会发现,两个线程的确都启动了,但是他们的系统资源(sum)确是相互独立的,诺想将其资源共享可将共享资源定义为静态类型(static)类型,或者让其实现Runnable接口完成对共享资源的访问,并且由于Java中每个类定义时只能继承一个父类,若让其继承Thread类来实现多线程,则必定会受到单继承局限的影响。
实现Runnable接口:
Runnable接口中只定义了一个方法:public void run()
所以实现Runnable接口只需实现此方法即可,例如将上述代码进行修改后:
package ClassPackage;
//实现Runnable接口 实现多线程
class RunnableTestimplements Runnable{ //实现Runnable接口
private int sum=10;
public void run()
{
for(;sum>0;sum--)
System.out.println(Thread.currentThread().getName()+" sum="+sum);
}
}
public class Test29 {
public static void main(String args[]){
//创建两个线程共享RunnableTest对象的资源,并启动
RunnableTest t=new RunnableTest();
Threadt1=newThread(t);
Threadt2=newThread(t);
t1.start();
t2.start();
}
}
运行结果:
细心的朋友可能已经发现怎么还是有重复值呢?
其实上面的程序的确已经实现了资源的共享,但是之所以出现重复值,正是线程中存在的一个必须注意的问题,即线程的同步问题。
同步与死锁:
出现以上问题的原因是:当一个线程正准备对共享资源进行操作时,忽然失去了CPU执行权,而另一个线程这时进入获得执行权对共享资源进行操作,紧接着又失去执行权,因此出现以上情况,类似这种情况都归根于对共享资源操作的安全问题。
一、同步代码块(对以上程序进行了更改,就是为了避免资源安全问题,在此不再细说,感兴趣的朋友可自行更改一遍查看原因):
package ClassPackage;
//实现Runnable接口 实现多线程
class RunnableTestimplements Runnable{ //实现Runnable接口
privateint sum=100;
publicvoid run()
{
for(;;)
synchronized(this){
if(sum>0)
System.out.println(Thread.currentThread().getName()+" sum="+sum--);
else break;
}
}
}
publicclass Test29 {
publicstatic void main(String args[]){
//创建两个线程共享RunnableTest对象的资源,并启动
RunnableTest t=new RunnableTest();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
t2.start();
}
}
package ClassPackage;
//实现Runnable接口 实现多线程
class RunnableTest implements Runnable{ //实现Runnable接口
private int sum=100;
public void run()
{
for(;;)
{
show();
}
}
public synchronized void show()
{
if(sum>0)
System.out.println(Thread.currentThread().getName()+" sum="+sum--);
}
}
public class Test29 {
public static void main(String args[]){
//创建两个线程共享RunnableTest对象的资源,并启动
RunnableTest t=new RunnableTest();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.start();
t2.start();
}
}
加入同步后运行结果:
锁:
上图中this就一个锁,那么我们究竟该如何理解锁的概念呢?
锁,我们可以简单的将其看作为一把钥匙,而同步则就是门,只有获得了钥匙的人(线程)才能进入门中,并且钥匙只有一把,只能当此时拿着钥匙的人(线程)放弃所有权,其他人(线程)才可进入其中。
何时放弃所有权,即:同步代码块或同步函数执行完成时。
同步函数默认锁为:this
静态同步函数默认锁:类名.class (该方法所在类的字节码对象,因为静态方法直接由类调用,无本类实例化对象)
(多线程扩展)单例设计模式:
懒汉式:实例的延迟加在,存在问题,当多线程访问时会出现安全问题(共享资源),加入同步来解决,同步代码块和 同步方法 都可,但会降低代码效率,可用双重“if“加快效率,代码如下:
class Single{ //懒汉式(延迟加载)单例设计模式
private static Single s=null;
private Single(){};
public static Single getSingle()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s=new Single();
}
}
return s;
}
}
饿汉式(多线程访问时,不存在安全问题):
class Single02{
private static final Single02 s2=new Single02();
private Single02(){}
public static Single02getSingle02(){
returns2;
}
}
死锁:同步中嵌套同步,使用的锁却不同时,可能会产生死锁状态。
实现个简单的死锁状态,代码如下:
package ClassPackage;
//设计出多线程死锁状态
class Deadlock implements Runnable{
private boolean flag;
public Deadlock(boolean flag){
this.flag=flag;
}
public void run(){ //实现Runnable接口 run()方法
if(flag){ //t1线程进入,并获取lock1锁
synchronized(Lock.lock1){
System.out.println("t1手持 lock1锁 --->向t2索要 lock2锁 ");
synchronized(Lock.lock2){
System.out.println("t1获得 lock2锁");
}
}
}else{//t2线程进入,并获得lock2锁
synchronized(Lock.lock2){
System.out.println("t2手持 lock2锁 --->向t1索要lock1锁");
synchronized(Lock.lock1){
System.out.println("t2获得 lock1锁");
}
}
}
}
}
class Lock{ //此类专门用于创建两把锁
static Lock lock1=new Lock(); //锁1
static Lock lock2=new Lock(); //锁2
}
public class Test27 {
public static void main(String args[])
{
//创建两个以Deadlock类为共享资源的线程
Thread t1=new Thread(new Deadlock(true));
Thread t2=new Thread(new Deadlock(false));
//启动线程
t1.start();
t2.start();
}
}
和谐状态(未产生死锁):
死锁状态:
所谓死锁,可以比喻为有两个人,每人只有一支筷子,两人都想吃饭桌上的菜,然而A对B说:“把你的筷子给我,我要吃饭“,而B同时对A说:“把你的筷子给我,我也要吃饭”,因此,两人互不相让,产生死锁,相互等待对方让出筷子。而和谐状态则是一人先让出筷子,让另一人吃饭,一定时间后另一人再让出筷子,如此相互交换,就不会出现程序跑死状态。
最后奉献多线程中的经典例题一道(生产者与消费者)链接:
生产者与消费者(线程同步与死锁、等待与唤醒)
http://bbs.itheima.com/thread-109994-1-1.html
(出处: 黑马程序员IT技术论坛)
---------------------- <ahref="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<ahref="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------