多线程


0. 线程概述:

  • 进程: 是一个正在执行中的程序. 每一个进程执行都有一个执行顺序. 该顺序是一个执行路径, 或者叫一个控制单元. 
  • 线程: 就是进程中的一个独立的控制单元. 线程在控制着进程的执行. 
  • 一个进程中至少有一个线程. 


1. 创建线程的方法:

  • 继承Thread类: 子类覆盖父类中的run方法, 将线程运行的代码存放在run中. 建立子类对象的同时线程也被创建. 通过调用start方法开启线程(start的两个作用: 启动线程, 调用run方法). 
  • 实现Runnable接口: 子类覆盖接口中的run方法. 通过Thread类创建线程, 并实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数. Thread类对象调用start方法开启线程. 
  • * 继承方式和实现方式有什么区别?
    实现方式好处: 避免了单继承的局限性. 在定义线程时, 建议使用实现方式. (还可以让多个线程处理同一个问题). 


2. 用Runnable接口例子:

/*
 * 需求:简单的卖票程序. 
 * 多个窗口同时卖票. 
 */

class Ticket implements Runnable
{
	private int tick = 100;
	public void run()
	{
		while(true)
		{
			if(tick>0)
				System.out.println(Thread.currentThread().getName() + " sale : " + tick--);
		}
	}
}

public class TicketDemo {

	public static void main(String[] args) {
		
		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();
	}

}

3. 线程的四种状态:

 新建(New)就绪(Runnable)→运行(Running)→阻塞(Blocked)→死亡(Dead)



4. 同步代码块

// 格式
synchronized(对象)
{
	需要被同步的代码
}

  • 对象如同所锁. 持有锁的线程可以再同步中执行, 没有持有锁的线程即使获取cpu的执行权, 也进不去, 因为没有获取锁.
  • 好处: 解决了多线程的安全问题. 弊端: 多线程需要判断锁, 较消耗资源. 


5. 同步函数

格式: 在函数上加上synchronized修饰符即可. 

* 同步函数用的锁是this. 

* 静态同步函数用的锁是该方法所在类的"类名.class"字节码文件对象(是Class类的实例). 


6. 死锁

定义: 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁. 

例程:

class Test implements Runnable
{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag = flag;
	}
	
	public void run()
	{
		if(flag)
		{
			(true)
			{
				synchronized(MyLock.locka)
				{
					System.out.println("if locka");
					synchronized(MyLock.lockb)
					{
						System.out.println("if lockb");
					}
				}				
			}
		}
		else
		{
			while(true)
			{
				synchronized(MyLock.lockb)
				{
					System.out.println("else lockb");
					synchronized(MyLock.locka)
					{
						System.out.println("else locka");					
					}
				}						
			}
		}
	}
}

// 提供锁
class MyLock
{
	static Object locka = new Object();
	static Object lockb = new Object();
}

// 测试死锁
public class DeadLockTest {

	public static void main(String[] args) {
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		
		t1.start();
		t2.start();		
	}

}


7.  * 线程间通信

wait(): 等待. 
notify(): 唤醒. 唤醒随机的一个等待线程. 
notifyAll(): 唤醒所有. 

  • 这三个方法都是用在同步中, 因为要对持有监视器(锁)的线程操作. 所以要使用再同步中, 因为只有同步才具有锁. 
  • 为什么这三个方法不是定义在Thread类中, 而是在Object类中? 
    答: 因为这些方法在操作同步中线程时, 都必须要表示它们所操作线程持有的锁, 一个锁上的被等待线程, 只可以被同一个锁上的notify唤醒, 不可以被不同锁中的线程唤醒. 
    也就是说, 等待和唤醒必须是同一个锁. 
    而锁可以是任意对象, 所以可以被任意对象调用, 定义在Object类中. 
例子: 
// Res的对象作为被操作的资源和锁
class Res
{
	String name;
	String sex;
	boolean flag = false;
}

class Input implements Runnable
{
	private Res r;
	Input(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			synchronized(r)
			{
				if(r.flag)
					// 调用锁r的wait()方法
					try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}
				if(x==0)
				{
					r.name = "mike";
					r.sex = "man";				
				}
				else
				{
					r.name = "丽丽";
					r.sex = "女女女女";
				}
				x = (x+1)%2;
				r.flag = true;
				// 调用锁r的notigy()方法
				r.notify();
			}
		}
	}
}

class Output implements Runnable
{
	private Res r;
	Output(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			synchronized(r)
			{
				if(!r.flag)
					// 调用锁r的wait()方法
					try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}
				System.out.println(r.name + "...." + r.sex);
				r.flag = false;
				// 调用锁r的notigy()方法
				r.notify();
			}
		}
	}
}

// 测试
public class InputOutputDemo {

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

}
上述代码很乱, 优化后是酱紫:
// Res的对象作为被操作的资源
class Res
{
	private String name;
	private String sex;
	boolean flag = false;
	
	public synchronized void set(String name, String sex)
	{
		if(flag)
			try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}
		this.name = name;
		this.sex = sex;
		flag = true;
		this.notify();
	}
	public synchronized void out()
	{
		if(!flag)
			try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}
		System.out.println(name+"......."+sex);
		flag = false;
		this.notify();
	}
}

class Input implements Runnable
{
	private Res r;
	Input(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			if(x==0)
				r.set("mike", "man");
			else
				r.set("丽丽", "女女女女");
			x = (x+1)%2;
		}
	}
}

class Output implements Runnable
{
	private Res r;
	Output(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}

// 测试
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();		
	}

}

8. 停止线程

因为开启多线程运行, 运行代码通常是循环结构, 只要控制住循环, 就可以让run方法结束, 也就是线程结束. 
就像这样: 
class StopThread implements Runnable
{
	private boolean flag = true;
	public void run()
	{
		while(flag)
		{
			System.out.println(Thread.currentThread().getName()+".....run");
		}
	}
	public void changeFlag()
	{
		flag = false;
	}
}


public class StopThreadDemo {

	public static void main(String[] args) {
		
		StopThread st = new StopThread();
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		
		t1.start();
		t2.start();
		
		int num = 0;
		while(true)
		{
			if(num++ == 60)
			{
				st.changeFlag();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"....."+ num);
		}
	}

}
* Thread类中提供了interrupt()方法, 用于中断sleep()、wait()和join()等. 


9. join()方法

作用: 当A线程执行到了B线程的.join()方法时, A就会等待. 等B线程都执行完, A才会执行. join可以用来临时加入线程执行. 

例子: 

class Demo1 implements Runnable
{
	public void run()
	{
		for(int x=0; x<70; x++)
		{
			System.out.println(Thread.currentThread().getName()+"..."+x);
		}
	}
}

public class Test2 {
	
	public static void main(String[] args) throws InterruptedException {	
	 
		Demo1 d = new Demo1();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();
		
		//t1.join();		//t1要申请加入. 就是t1要加进来,抢走申请cpu执行权. 主线程运行到这句, 然后等待t1结束才能继续执行. 
		
		t2.start();
		
		t1.join();			//主线程释放执行权给t1. 执行这句后, t1和t2交替执行, 直到t1执行完后主线程又拿到执行权. 
		
		for(int x=0; x<80; x++)
		{
			System.out.println("main....." + x);
		}
		System.out.println("over");
	}
}

10. yield()方法

作用: 暂停正在执行的线程对象, 并执行其他线程. 

  • yield将线程转为就绪状态. 只是让线程暂停一下, 如果程序中不存在优先级比该线程高或相同的线程, 则此让步的线程继续运行; 如果有, 另算 (给优先级更高的线程让步). 
  • sleep() 方法声明抛出InterruptedException异常, yield() 方法没抛出异常. 
例子: 
public class YieldTest extends Thread
{
	public YieldTest(String name)
	{
		super(name);
	}
	public void run()
	{
		for (int i = 0; i < 50; i++)
		{
			System.out.println(getName() + " " + i);
			if (i == 20)
			{
				Thread.yield();
			}
		}
	}
	public static void main(String[] args) throws Exception
	{
		YieldTest yt1 = new YieldTest("HIGH");
		//yt1.setPriority(Thread.MAX_PRIORITY);
		yt1.start();
		YieldTest yt2 = new YieldTest("LOW");
		//yt2.setPriority(Thread.MIN_PRIORITY);
		yt2.start();
	}
}





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值