黑马程序员_Java_多线程

本文详细介绍了Java多线程的基本概念、线程的创建方式、Thread类常用方法、多线程的安全问题及死锁解决、线程间通讯机制等核心内容。通过示例代码演示了如何使用匿名内部类实现两种线程创建方式,以及如何解决多线程同步问题,最终提供了线程间通讯的两种方式:早期等待唤醒和JDK1.5后的Lock与Condition解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、多线程概述
1、进程、线程与多线程
        进程是一个正在执行的程序,每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。比如虚拟机启动时有一个java.exe的执行程序就是一个进程
        线程属于某个进程,不能自动运行,要由进程启动执行,进行控制。线程是进程中的一个独立的控制单元,线程在控制着进程的执行,一个进程中至少有一个线程。每个独立线程代表一个独立操作。
一个进程中有多个线程,称为多线程。虚拟机启动的时候就是多线程JVM 启动不止一个线程至少有:一个主线程和一个负责垃圾回收的线程。多线程提高了程序的运行效率,但线程太多会导致效率的降低,因为线程的执行依靠的是CPU的来回切换。

二、线程的创建方式
线程有两种创建方式,分别是继承Thread类和实现Runnable接口。
2.1  继承Thread类的创建步骤:
定义类继承Thread类;
⑵复写Thread中的run方法,用于存储线程要执行的代码;
⑶调用线程的start方法启动线程。
代码示例:
<span style="font-size:12px;">package demo.thread;

public class ThreadDemo {

	public static void main(String[] args) {
		Demo demo=new Demo();
		demo.start();//3,调用线程的start方法启动线程
	}

}

class Demo extends Thread{//1,定义类继承Thread	

	public void run() {//2,复写Thread中的run方法,用于存储线程要执行的代码
		System.out.println("demo run");
		System.out.println(this.getName());
		return;
	}
	

}</span>

2.2   实现Runnable接口的创建步骤:
定义类实现Runnable接口;
⑵复写Runnable接口中的run方法,用于存储线程要执行的代码;
⑶创建自定义的Runnable的子类对象;
⑷将Runnable接口的子类对象作为参数传给Thread类的构造函数
⑸调用Thread类的start方法开启线程。
代码示例:
<span style="font-size:12px;">package demo.thread;

public class ThreadDemo {

	public static void main(String[] args) {
		Demo demo=new Demo();//3,创建自定义的Runnable的子类对象;
		Thread t=new Thread(demo);//4,将Runnable接口的子类对象作为参数传给Thread类的构造函数;
		t.start();//5,调用Thread类的start方法开启线程。
	}

}

class Demo implements Runnable{//1,定义类实现Runnable接口;

	public void run() {//2,复写Runnable接口中的run方法,用于存储线程要执行的代码;
		System.out.println("demo run");
		return;
	}
	

}</span>

2.3 用匿名内部类实现两种线程:
用匿名内部类继承Thread类和实现Runnable接口,方便代码的书写。
代码示例:
<span style="font-size:12px;">package demo.thread;

public class ThreadTestDemo {

	public static void main(String[] args) {
		new Thread(){//匿名内部类继承Thread类
			public void run(){//复写Thread中的run方法,用于存储线程要执行的代码;
				for(int x=0;x<100;x++){
					System.out.print(Thread.currentThread().getName()+",..."+x);
					
				}
			}
		}.start();//调用线程的start方法启动线程。
		
		//定义类实现Runnable接口;
		new Thread(/*创建自定义的Runnable的子类对象;将Runnable接口的子类对象作为参数传给Thread类的构造函数*/
				new Runnable(){

			public void run() {//复写Runnable接口中的run方法,用于存储线程要执行的代码;
				for(int x=0;x<100;x++){
					System.out.print(Thread.currentThread().getName()+",..."+x);
					
				}
			}
		}).start();//调用Thread类的start方法开启线程。
		
		for(int x=0;x<100;x++){
			System.out.print(Thread.currentThread().getName()+",..."+x);
			
		}
	}

}
</span>


两种创建方式的区别:
继承Thread类:线程代码存放于Thread子类的run方法中,无法继承其他类。
实现Runnable接口:线程代码存放于Runnable子类的run方法中,避免了单继承的局限性,定义线程时,推荐使用实现的方法。

三、 Thread类常用方法
获取当前线程对象,通过currentThread()方法获取当前线程对象
<span style="font-size:12px;">    System.out.print(Thread.currentThread());</span>
获取线程名字,通过getName()方法获取线程对象的名字
<span style="font-size:12px;">    System.out.print(Thread.currentThread().getName());
</span>
设置线程名字,通过setName()方法获取线程对象的名字
<span style="font-size:12px;">    Thread.currentThread().setName("Java多线程");</span>
⑷休眠,sleep()方法
<span style="font-size:12px;">    Thread.sleep(10);//休眠10毫秒</span>
⑸守护setDaemon(),设置一个线程为守护线程,该线程不会单独执行,当其他非守护线程都结束后自动退出
<span style="font-size:12px;">    Thread.setDaemon(true);//设置为守护线程</span>
⑹加入join(),当前线程暂停,等待指定的线程执行结束后,当前线程再继续
<span style="font-size:12px;">    Thread.join();//等待该线程结束后,当前线程继续执行</span>
⑺yield()让出CPU,暂停当前正在执行的线程对象,并执行其他线程。
<span style="font-size:12px;">    Thread.yield();//<span style="white-space: pre;">暂停当前正在执行的线程对象,并执行其他线程</span></span>
⑻setPriority()设置线程的优先级,加大线程CPU运行几率。
<span style="font-size:12px;">    Thread.setPriority(Thread.MAX_PRIORITY);//设置最高优先级</span>

四、多线程的安全问题
多线程的运行出现安全问题的原因:当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:对多条操作共享数据的余军,只能让一个线程执行完。在执行过程中,其他线程不可以参与执行。java提供了专业的解决方法:同步代码块和同步函数。
同步代码块使用的锁是对象,其结构为:
<span style="font-size:12px;">    synchronized(对象){
    //被同步的代码
    }</span>
非静态同步函数使用的锁是this,静态函数使用的锁是类.class,其结构为:
<span style="font-size:12px;">    public (static) synchronized  void 函数(){
	//被同步的代码
    }</span>
        同步的前提:必须要有两个或两个以上的线程;必须是多个线程使用同一个锁;必须保证同步中只能有一个线程在运行。
同步解决了多线程的安全问题,但多个线程需要判断锁,较为消耗资源。

以下为同步代码块和同步函数嵌套的代码示例:
<span style="font-size:12px;">package demo.thread;

public class TicketDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TickedThread p=new TickedThread();
		
		Thread t1=new Thread(p);//两个线程购票
		Thread t2=new Thread(p);
		t1.start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		p.flag=false;
		t2.start();
		
		
	}

}
//购票实例
class TickedThread implements Runnable{//extends Thread{
	private static int tick=300;//300张票
	boolean flag=true;
	Object mutex=new Object();
	public  void run() {
		
		if(flag)
		while(true)
			synchronized (mutex) {//同步代码块
				buy();//同步函数
			}
		else 
			while(true)
				buy();

		
	}
	
	public  synchronized void buy(){//同步函数
		
		synchronized (mutex) {//同步代码快
			if(tick>0)
				System.out.println(Thread.currentThread()+"code :"+tick--);
		}
	}
	
	
}</span>

五、死锁
同步中嵌套同步,使用了互斥锁,造成互相等待,应避免使用不同锁。
代码示例:
<span style="font-size:12px;">package demo.thread;
//死锁实例
public class DeadLockDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread t1=new Thread(new Test(true));
		Thread t2=new Thread(new Test(false));
		t1.start();
		t2.start();//使用不同锁,容易造成死锁
	}

}

class Test implements Runnable{
	private boolean flag;
	Test(boolean flag){
		this.flag=flag;
	}
	public void run(){
		if(flag){
			synchronized(MyLock.locka){//使用了锁A
				System.out.println("if locka");
				synchronized(MyLock.lockb){
					System.out.println("if lockb");
					
				}
			}
		}else{
			synchronized(MyLock.lockb){//使用了锁B
				System.out.print("else lockb");
				synchronized(MyLock.locka){
					System.out.print("else locka");
					
				}
			}
		}
	}
}

class MyLock{
	static Object locka=new Object();
	static Object lockb=new Object();
			
}</span>
六、线程间通讯
线程间通讯是多个线程在操作同一资源,但操作的动作不同。
1、早期等待唤醒的线程间通讯方式
      wait():在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待。换句话说,此方法的行为就好像它执行wait(0)调用一样。
      notify():唤醒在此对象监视器上等待的单个线程。
notifyAll():唤醒在此对象监视器上等待的所有线程。
下面为一个输出交替变化的线程间通讯代码示例:
<span style="font-size:12px;">package demo.thread;
//线程间通讯,输入输出交替变化
public class InputOutputDemo {

	public static void main(String[] args) {
		Res r=new Res();
		new Thread(new Input(r)).start();
		new Thread(new Output(r)).start();
	}

}

class Res{//同一操作资源
	private String name;
	private String sex;
	private boolean flag=false;
	public synchronized void set(String name,String sex){//同步函数设置姓名性别
		if(flag)
			try {
				wait();//等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		this.name=name;
		this.sex=sex;
		flag=true;
		this.notify();//唤醒
	}
	public synchronized void out(){//输出姓名性别
		if(!flag){
			try {
				wait();//等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(name+"--------"+sex);
		flag=false;
		this.notify();//唤醒
	}
}

class Input implements Runnable{

	Res r;
	Input(Res r){
		this.r=r;
	}
	public void run() {
		// TODO Auto-generated method stub
		int x=0;
		while(true){
		
					if(x==0)
			r.set("mike","man");
			else
			r.set("lili","woman");
			
			x=(x+1)%2;//名字性别交替变化
		
		}
	}
	
}

class Output implements Runnable{
	
	Res r;
	Output(Res r){
		this.r=r;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){					
			r.out();
		}
	}
	
}</span>
2、JDk1.5后提供了等待唤醒升级方案:
使用Lock替代synchronized同步,使用Condition对象的await()、single()、singleAll()方法替代Object中的wait()、notify()、notifyAll()方法。
代码示例:
<span style="font-size:12px;">package demo.thread;
//生产者和消费者实例
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ProducerConsumerDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Resource r=new Resource();
		new Thread(new Producer(r)).start();
		new Thread(new Producer(r)).start();
		new Thread(new Consumer(r)).start();
		new Thread(new Consumer(r)).start();
	}

}

class Resource{
	private String name;
	private int count=1;
	private boolean flag=false;
	private Lock lock=new ReentrantLock();
    private Condition con=lock.newCondition();
	public  void set(String name){
		lock.lock();//锁住
		
			try {while(flag)
				con.await();//等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		finally{
		this.name=name+"--"+count++;
		System.out.println(Thread.currentThread().getName()
				+"...生产者"+this.name);
		flag=true;
		con.signalAll();//唤醒所有等待线程
		lock.unlock();//解锁
		}
	}
	public   void out(){
		lock.lock();//锁住
		
			try {while(!flag)
				con.await();<span style="font-family: Arial, Helvetica, sans-serif;">//等待</span>
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		finally{
		System.out.println(Thread.currentThread().getName()
				+"...消费者"+this.name);
		flag=false;
		con.signalAll();//唤醒所有等待线程
		lock.unlock();//解锁
		}
	}
}

class Producer implements Runnable{//生产者
	private Resource res;
	
	Producer(Resource res){
		this.res=res;
	}
	public void run(){
		while(true){
			res.set("+商品+");
		}
	}
}

class Consumer implements Runnable{//消费者
	private Resource res;
	
	Consumer(Resource res){
		this.res=res;
	}
	public void run(){
		while(true){
			res.out();
		}
	}
}</span>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值