Java基础之多线程

一、线程简介

要先了解线程就得知道进程。进程就是正在执行的程序,也就是程序执行的路径。线程就是进程中的独立控制单元,线程控制着进程的执行,一进程至少有一个线程,当有多个线程时,每个线程完成一个功能,并与其他线程并发执行,这种机制就叫多线程。当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();
		
	}

}

运行结果为:


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值