java基础复习之多线程Thread 十三

 

多线程:就是应用程序多条执行路径
进程:正在运行的程序,

 

  线程: 进程的执行单元,一条执行路径

 

 

线程的简单使用方式一:

 

package thread;
/**
 * 
 * @author Angus
 *	getName() 返回该线程的名称。
 */
public class MyThread extends Thread {
	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {
			
			System.out.println(getName()+"hello");
		}
	}
}


ThreadDemo测试;

 

 

 

 

package thread;
/**
 * @author Angus
 *	
 *	多线程:就是应用程序多条执行路径
 *		进程:正在运行的程序,
 *		线程: 进程的执行单元,一条执行路径
 *	
 *	实现?
 *		线程类Thread:
 *			A;定义一个类基础Thread类,重写run方法,start()方法启动线程。
 *			B:实现Runnable接口,重写run方法
 *	
 */
public class ThreadDemo{

	public static void main(String[] args) {
		MyThread mt = new MyThread();
		MyThread mt2 = new MyThread();
		mt.start();
//		mt.start(); //Exception in thread "main" java.lang.IllegalThreadStateException
//		//连续开始两次线程报错。。分析异常开启两个线程需要两个线程对象
		
		mt.setName("zzz");
		mt2.setName("ddd");
		mt2.start();
	}
	
	
}


线程的简单使用方式二;

 

 

package thread;
/**
 * 	
 * @author Angus
 *	B:实现Runnable接口,重写run方法
 */
public class MyRunnableDemo {

	public static void main(String[] args) {
		MyRunnable my = new MyRunnable();
		//数显runnable的发类没有start的方法,启动必须启动start方法
		//所以需要考虑Thread类
		Thread t = new Thread(my);
		Thread t2 = new Thread(my);
		
		t.setName("xxxx");
		t2.setName("wwww");
		t.start();
		t2.start();
	}

}


MyRunnable类:

 

 

package thread;

/**
 * @author Angus 实现Runnable接口,重写run方法 public static Thread currentThread()
 *         返回对当前正在执行的线程对象的引用。
 */
public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {

			System.out.println(Thread.currentThread().getName() + "  hello");
		}
	}

}

运行结果差不多。。。。
 

 

会发现第一种每次需要new两个对象,创建两个线程,走了两次run,第二种只创建了一个资源对象,这样数据和操作分离更好一些,一般开发中使用第二种多一些。。。

 

线程的生命周期:

 

多线程应用的场景:

运行都会卡死,建议DOS测试,这样可以关闭,eclipse只能关闭软件了。。。

方式一:

 

package thread2;
/**
 * 
 * @author Angus
 *	多线程应用的场景
 *		窗口卖票
 *	
 *	有一趟火车,票不多了,还有200张,四个窗口卖票
 *	
 *	使用多线程模拟卖票
 *		方式1 继承Thread类
 *		方式2实现Runnable接口
 *	
 */
public class ThreadTest {
	public static void main(String[] args) {
		TicketTread tt1 = new TicketTread();
		TicketTread tt2 = new TicketTread();
		TicketTread tt3 = new TicketTread();
		TicketTread tt4 = new TicketTread();
		
		tt1.setName("窗口1");
		tt2.setName("窗口2");
		tt3.setName("窗口3");
		tt4.setName("窗口4");
		
		tt1.start();
		tt2.start();
		tt3.start();
		tt4.start();
	}
	
}


TicketTread

package thread2;
/**
 * @author Angus
 *	方式一
 */
public class TicketTread extends Thread {
	@Override
	public void run() {
		int tickets = 200;
		
		while(true){
			if(tickets>0){
				System.out.println(getName()+" 正在出售"+(tickets--)+"张票");
			}
		}
	}
}


这样运行会CPU报表。。。直接死机,

 

原因: 每个线程new的时候都是一个新的200,都走run方法,,死循环。。

改进;

TicketTread

 

package thread2;
/**
 * @author Angus
 *	方式一
 */
public class TicketTread extends Thread {
	private static int tickets = 200;
	@Override
	public void run() {
		
		
		while(true){
			if(tickets>0){
				System.out.println(getName()+" 正在出售"+(tickets--)+"张票");
			}
		}
	}
}


而static修饰的成员变量比较占用资源,去掉更换第二种方式测试

 

 

package thread2;
/**
 * 
 * @author Angus
 *	方式二
 */
public class TicketRunnanble implements Runnable {
	private int tickets = 200;
	@Override
	public void run() {

		while(true){
			if(tickets>0){
				System.out.println(Thread.currentThread().getName()+" 正在出售"+(tickets--)+"张票");
			}
		}
	}

}


Runnable测试:

 

 

package thread2;
/**
 * 
 * @author Angus
 *	方式二
 */
public class RunnableTest {

	public static void main(String[] args) {
		TicketRunnanble tr = new TicketRunnanble();
		
		Thread t1 = new Thread(tr); 
		Thread t2 = new Thread(tr); 
		Thread t3 = new Thread(tr); 
		Thread t4 = new Thread(tr); 
		
		t1.setName("窗口1");
		t2.setName("窗口2");
		t3.setName("窗口3");
		t4.setName("窗口4");
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}


运行发现也会出现卡死的现象,和之前一样出现一样的票卖了多次的情况。

 

加一个睡眠操作:

 

package thread2;
/**
 * 
 * @author Angus
 *	方式二
 */
public class TicketRunnanble implements Runnable {
	private int tickets = 200;
	@Override
	public void run() {

		while(true){
			
			if(tickets>0){
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+" 正在出售"+(tickets--)+"张票");
			}
		}
	}

}

 

 

 

 

 

会发现出现了负数:

 

甚至还有-2

当剩余最后一张票的时候,线程判断 >0 ,四个线程都进来了,这样就会出现负数的情况在 t--的情况下。而且在ticket--的情况下 也会出现相同票数的情况。。

 

解决:引入线程锁  synchronized

 

package thread2;

/**
 * 
 * @author Angus 
 * 			解决出现负数和相同数的方法; 
 * 			在出问题的代码中加一个锁 找问题代码: 
 * 				1 找共享数据 
 * 				2 共享数据中有没有多条语句 
 * 				3是不是在多线程中 
 * 怎么锁? 
 * 		synchronized(锁对象)
 * 注意;多个线程必须使用同一把锁
 */
public class TicketRunnanble implements Runnable {
	private int tickets = 100;
	private Object obj = new Object();
	@Override
	public void run() {

		while (true) {
			synchronized (obj) {

				if (tickets > 0) {
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()
							+ " 正在出售" + (tickets--) + "张票");
				}
			}
		}
	}

}


这样运行就没有问题了,不会出现负数和相同的数据了。

 

方法锁的引入:

 

package thread2;

/**
 * 
 * @author Angus synchronized(锁对象)
 * 
 *         锁对象除了object还可以试其它对象? 结论; 同步代码锁可以试任意对象
 */
class Demo {
	// 测试发现没问题。
}

public class TicketRunnanble implements Runnable {
	private int tickets = 100;
	private Object obj = new Object();
	private Demo d = new Demo();

	@Override
	public void run() {
		int x = 0;
		while (true) {
			if (x % 2 == 0) {
				synchronized (d) {

					if (tickets > 0) {
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()
								+ " 正在出售" + (tickets--) + "张票");
					}
				}
			} else {
				synchronized (d) {

					if (tickets > 0) {
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()
								+ " 正在出售" + (tickets--) + "张票");
					}
				}
			}
			x++;
		}

	}
}


测试没有问题,同一个锁对象。。。把else抽取成方法测试;

 

 

package thread2;

/**
 * 
 * @author Angus synchronized(锁对象)
 * 
 *         锁对象除了object还可以试其它对象? 结论; 同步代码锁可以试任意对象
 */
class Demo {
	// 测试发现没问题。
}

public class TicketRunnanble implements Runnable {
	private int tickets = 100;
	private Object obj = new Object();
	private Demo d = new Demo();

	@Override
	public void run() {
		int x = 0;
		while (true) {
			if (x % 2 == 0) {
				synchronized (d) {

					if (tickets > 0) {
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()
								+ " 正在出售" + (tickets--) + "张票");
					}
				}
			} else {
				check();
			}
			x++;
		}

	}

	private void check() {

		synchronized (d) {

			if (tickets > 0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()
						+ " 正在出售" + (tickets--) + "张票");
			}
		}
	
	}
}


这样运行也没有问题,OK,线程抽取方法锁测试;

 

 

private synchronized void check() {

			if (tickets > 0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()
						+ " 正在出售" + (tickets--) + "张票");
			}
	}

方法中加了锁,代码中就不需要加了,测试。。:

 


 

出现了一样的,因为方法锁也没有确认同一个对象,if中用的是demo对象。。

问题一:测试返现同步方法锁的对象为this对象修改代码:

 

synchronized (this)


这样就和同步方法为同一个对象,线程就没有问题了。。

 

问题二:静态方法锁的对象是?

测试返现为:

是当前类的字节码文件对象
类名.class

修改代码:

 

synchronized (TicketRunnanble.class) 

 

 

 

 

 

线程的死锁:


MyLock

 

package thread2;

public class MyLock {
	public static final Object obja = new Object();
	public static final Object objb = new Object();
}


DieLock

 

 

 

 

package thread2;
/**
 * @author Angus
 * 	
 *	死锁的问题:
 *		五个人吃饭,菜齐了,一人只有一只筷子,每个人思考,把筷子借给别人,然后别人吃完自己再吃,
 *		假如五个人都饿了,都拿着自己的筷子,并且等着别人放下筷子,然后使用,但是没有一个人愿意先放下筷子
 *		所以就出现了死锁。。
 *	
 */
public class DieLock extends Thread{
	private boolean flag;
	
	public DieLock(boolean flag) {
		this.flag = flag;
	}
	@Override
	public void run() {
		if(flag){
			synchronized (MyLock.obja) {
				System.out.println("true---obja");
				//当线程d1走到这里线程2抢到了起源  线程d1需要 MyLock.objb
				synchronized (MyLock.objb) {
					System.out.println("true---objb");
				}
			}
		}else{
			synchronized (MyLock.objb) {
				System.out.println("true---objb");
				//当线程d2走到这里的时候,线程d2需要 MyLock.obja
				synchronized (MyLock.obja) {
					System.out.println("true---obja");
				}
			}
		}
		//谁也不放锁,就会造成死锁。。。。。
	}
	
}


测试DieLockDemo

 

 

package thread2;

public class DieLockDemo {

	public static void main(String[] args) {
		DieLock d1 = new DieLock(true);
		DieLock d2 = new DieLock(false);
		
		d1.start();
		d2.start();
	}

}


结果;  出现了死锁。。。。

 

 

 

线程间的通讯:

线程间的通讯:不同种类的线程对共享数据的操作功能。。。

 

 * 举例:以学生做为资源
 * 学生类
 * 设置学生类
 * 获取学生类
 * 测试类

 

Student

package thread3;

public class Student {
	String name;
	int age;
}

SetStudent

package thread3;

public class SetStudent implements Runnable {
	private Student s;
	
	public SetStudent(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		int x = 0;
		while(true){
			if(x %2 ==0){
				s.name = "呵呵1";
				s.age = 28;
			}else{
				s.name = "哈哈2";
				s.age = 11;
			}
		}
	}

}

GetStudent

package thread3;

public class GetStudent implements Runnable{
	private Student s;
	public GetStudent(Student s) {
		this.s = s;
	}

	@Override
	public void run() {
		while(true){
			
			System.out.println(s.name+s.age);
		}
	}

}


StudentTest

package thread3;
/**
 * @author Angus
 *	线程间的通讯:
 *		举例:以学生做为资源
 *	学生类
 *	设置学生类
 *	获取学生类
 *	测试类
 *	
 *测试发现,出现年龄和姓名的交叉问题。。
 */
public class StudentTest {
	public static void main(String[] args) {
		Student s = new Student();
		
		SetStudent ss = new SetStudent(s);
		GetStudent gs = new GetStudent(s);
		
		Thread t1 = new Thread(ss);
		Thread t2 = new Thread(gs);
		
		t1.start();
		t2.start();
	}
}


结果:

 


测试发现,出现年龄和姓名的交叉问题。。在run方法下,线程的随机性,出现了这样的情况。。

需要同步锁解决。

 

	@Override
	public void run() {
		//t1进来
		int x = 0;
		while(true){
			synchronized (s) {
				if(x %2 ==0){
					s.name = "呵呵 ";
					s.age = 28;
				}else{
					s.name = "哈哈 ";
					s.age = 11;
				}
			}
			x++; //x=1
		}
	}

 

 

 

@Override
	public void run() {
		while(true){
			synchronized (s) {
				System.out.println(s.name+s.age);
			}
		}
	}


运行解决。。。

但是发现在控制台一打一大片一个线程的数据,能不能每个线程交叉进程呢?

线程的等待:

 

*	发现输出的内容是一大片一大片输出的,能不能一个输出后再输出另一个交叉进行呢?
 *	
 *	针对输出:
 *		判断是否有数据,有就输出,没有待设置数据
 *	针对设置:
 *		判断是否有数据,有就等待输出,否则输出
 *	等待唤醒机制;
 *		wait();  //等待
 *		notify();//唤醒


修改代码:

 

Student;

 

package thread3;

public class Student {
	String name;
	int age;
	boolean flag = false; //ture 有数据
}


SetStudent

 

 

	@Override
	public void run() {
		//t1进来
		int x = 0;
		while(true){
			synchronized (s) {
				//如果有数据等待
				if(s.flag){
					try {
						s.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				if(x %2 ==0){
					s.name = "呵呵 ";
					s.age = 28;
				}else{
					s.name = "哈哈 ";
					s.age = 11;
				}
				//修改标记
				s.flag = true;
				s.notify();
			}
			x++; //x=1
		}
	}

GetStudent

	@Override
	public void run() {
		while(true){
			synchronized (s) {
				if(!s.flag){
					try {
						s.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println(s.name+s.age);
				s.flag = false;
				s.notify();
			}
		}
	}


运行结果;全部交叉进行。。。。

 



 

线程的优先级;

PriorityDemo

 

package thread4;

public class PriorityDemo implements Runnable{

	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+"  ----"+i);
		}
	}

}


PriorityTset

package thread4;
/**
 * 线程的优先级
 * @author Angus
 *	
 *	getPriority() 返回线程的优先级。
 *	setPriority(int newPriority)  更改线程的优先级。
 *
 *	运行发现,优先级并不是每次都最先运行
 *	注意:优先级只是在一定的程度上,让线程优先级的多执行次数
 */
public class PriorityTset {

	public static void main(String[] args) {
		PriorityDemo pd = new PriorityDemo();
		
		Thread t1 = new Thread(pd);
		Thread t2 = new Thread(pd);
		Thread t3 = new Thread(pd);
		
		t1.setName("哈哈1");
		t2.setName("哈哈2");
		t3.setName("哈哈3");
		
		t1.setPriority(1); //默认5范围1-10
		t2.setPriority(5); 
		t3.setPriority(10); 
		
		System.out.println(t1.getPriority());
		System.out.println(t2.getPriority());
		System.out.println(t3.getPriority());
		
		t1.start();
		t2.start();
		t3.start();
	}

}


其它API中的方法:

 

public static void yield()暂停当前正在执行的线程对象,并执行其他线程。 

让线程更和谐一些,一般不怎么用。。。。wait线程唤醒可以替换。。

public final void join()
                throws InterruptedException等待该线程终止。 

也是加入线程,可以让一个线程加入到运行中的线程中。。。。。前提是线程启动start了。。

public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 
该方法必须在启动线程前调用。 


该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)。 

 



最后附上JDK使用文档API 下载

 

 

 

 

 

 

 

最后附上JDK使用文档API  下载
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值