JAVA---多线程

1:多线程
(1)线程是依赖于进程而存在的。
A:进程 正在运行的应用程序
B:线程 进程的执行路径,执行单元

(2)多线程的两种方案:
请参照:多线程两种方式
继承Thread类(查看api简单介绍Thread类):
实现Runable接口:
package com.edu_02;
/**
 * 继承Thread类(查看api简单介绍Thread类):
 *
 */
public class MyThread extends Thread{
	//1.继承Thread类
	//2.重写run方法,重写run方法中的代码之后,当我们启动了这个线程之后,我们的这个线程就会执行run方法中的代码
	@Override
	public void run() {
		//需求:开启该线程之后,执行一个for循环
		for (int i = 0; i < 10; i++) {
			System.out.println(i);
		}
	}
	
}


package com.edu_02;

public class Test {
	public static void main(String[] args) {
		//只要我们创建了一个线程对象,并且启动该线程的实例,我们就相当于开启了一个线程
		MyThread mt = new MyThread();
		mt.start();//1.开启了一个线程   2.让开启的这个线程执行他对应的类中的run方法
		
		//在次创建一个子线程,并开启这个子线程执行他的run方法
		MyThread mt2 = new MyThread();
		mt2.start();
		
	}

}
package com.edu_03;
/**
 * 实现Runable接口:
 *
 */
public class MyThread implements Runnable{
	@Override
	public void run() {
		//启动该线程对象之后,需要执行的代码
		for (int i = 0; i < 10; i++) {
			System.out.println(i);
		}	
	}

}

package com.edu_03;

public class Test {
	public static void main(String[] args) {
		//创建Mythread对象
		MyThread mt = new MyThread();
		//开启这个线程
		//mt.start();//这里的这个类仅仅是实现了Runnalble接口的一个类,但是start方法在Thread类中
		//但是我们想要开启一个线程,就必须调用start方法,请问怎么办?
		
		//public Thread(Runnable target)
		Thread t1 = new Thread(mt);
		t1.start();
		
		
	}

}



(3)多线程的几个问题:
A:启动线程用的是哪个方法
start()
B:start()和run()的区别
start():1.开启线程  2.执行run()方法里面的代码
run():执行的是线程里面执行的代码,并不会开启线程
C:为什么要重写run()
因为每个线程需要执行的代码都是都是不一样的,
我们需要将每个线程自己独立执行的代码写到run()方法中执行
D:线程可以多次启动吗
可以

public class Test {
	public static void main(String[] args) {
		//B:start()和run()的区别
		// 创建一个线程独享
		MyThread mt = new MyThread();
		MyThread mt2 = new MyThread();
		//mt.start();//1.首先开启了一个独立的线程 2.让这个独立的线程执行run方法中的代码
		
		System.out.println("--------");
		//mt.run();//1.普通的创对象,调方法  2.代码实在主线程执行,不会开辟新线程
		
		
		//D:线程可以多次启动吗
		mt.start();
		//mt2.start();//如果是不同的线程对象,是可以同时开启的
		//mt.start();//线程是不能多次启动的
		
	}

}
package com.edu_04;

public class MyThread extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			System.out.println(i);
		}
	}

}



(4)线程的调度和控制
线程休眠(Thread.sleep(毫秒值))
线程名称(setName(),getName();)
线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间)
什么叫线程优先级:说白了就是设置你抢占cpu执行权抢占到的概率
package com.edu_05;

public class MyThread extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			//线程休眠(Thread.sleep(毫秒值))
			try {
				Thread.sleep(1000);//在此处出现的异常我们只能抓取,不能抛出
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//获取执行线程的姓名
			System.out.println(this.getName()+i);
		}
	}
}

package com.edu_05;
public class Test {
	public static void main(String[] args) {
		//线程名称(setName(),getName();)
		//创建3个线程对象
		MyThread t1 = new MyThread();
		MyThread t2 = new MyThread();
		MyThread t3 = new MyThread();
		
		//给三个线程设置姓名
		t1.setName("刘备");
		t2.setName("张飞");
		t3.setName("关羽");
		
		//设置线程的优先级
		//线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间)
		//t1.setPriority(100);//设置的区间必须在1-10之间
		t1.setPriority(10);
		
		//开启线程
		t1.start();
		t2.start();
		t3.start();
		
		
		
		
	}

}


(5)多线程案例(两种方式实现,睡一会出现线程安全问题):
5.1继承Thread卖票
5.2实现Runnable卖票(睡一会出现线程安全问题)
package com.edu_07;
//5.1继承Thread卖票
public class Test {
	public static void main(String[] args) {
		//创建三个线程模拟三个售票窗口
		MyThread mt1 = new MyThread();
		MyThread mt2 = new MyThread();
		MyThread mt3 = new MyThread();
		
		//给线程设置名称
		mt1.setName("窗口一");
		mt2.setName("窗口二");
		mt3.setName("窗口三");
		
		//启动线程,开启售票
		mt1.start();
		mt2.start();
		mt3.start();
		
	}

}

package com.edu_07;

public class MyThread extends Thread{
	//共有100张票,将ticket改为静态之后,被类的所有对象所共享
	static int ticket = 100;
	
	@Override
	public void run() {
		//用一个while true循环模拟三个窗口一直处于打开的状态
		while (true) {
			//只有当ticket>0的时候,才可以出售票
			if (ticket>0) {
				System.out.println(getName()+"正在出售第:"+ticket--+"张票");
			}
		}
	}
}

package com.edu_08;

public class Test {
	public static void main(String[] args) {
		//创建MyThread这个对象
		MyThread mt = new MyThread();
		
		//创建3个窗口
		Thread t1 = new Thread(mt);
		Thread t2 = new Thread(mt);
		Thread t3 = new Thread(mt);
		
		//给线程对象设置名称
		t1.setName("窗口一");
		t2.setName("窗口二");
		t3.setName("窗口三");
		
		//启动窗口开始售票
		t1.start();
		t2.start();
		t3.start();
		
	}

}

package com.edu_08;

public class MyThread implements Runnable{
	int ticket = 100;
	
	@Override
	public void run() {
		while (true) {
			if (ticket>0) {
				System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
			}
		}
	}

}


问题:
按照真实的情景加入了延迟,确发现出现了这样的两个问题:
  A:相同的票卖了多次
  CPU的一次操作必须是原子性的(操作是CPU执行一次就可以直接完成的)
  B:出现了负数的票
  随机性和延迟导致的
出现上面的问题称为线程安全问题。

(6)多线程安全问题
A:是否是多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据

(7)如何解决多线程安全问题(掌握)
* 线程安全执行效率就低
A:同步代码块(测试不是同一个锁的情况,测试是同一个锁的情况)
synchronized(对象) {
需要被同步的代码。
}
需求:1.测试不是同一把锁的时候线程安全吗? 2.如果是同一把锁线程安全吗?
两个问题:1.对象是什么 ?
 答:任意对象 ,相当于是一把锁,只要线程进去就把锁锁上
 2.需要同步的代码?
 答:被线程执行的代码
package com.edu_10;

public class Test {
	public static void main(String[] args) {
		//创建MyThread对象
		MyThread mt = new MyThread();
		
		//创建三个窗口
		Thread t1 = new Thread(mt);
		Thread t2 = new Thread(mt);
		Thread t3 = new Thread(mt);
		
		//给每一个窗口设置姓名
		t1.setName("窗口一");
		t2.setName("窗口二");
		t3.setName("窗口三");
		
		//开启窗口进行售票
		t1.start();
		t2.start();
		t3.start();
		
	}

}

package com.edu_10;
public class MyThread implements Runnable{
	//定义100张票
	static int ticket = 100;
	Object obj = new Object();
	int x = 0;
	
	@Override
	public void run() {
		while (true) {
			if (x%2==0) {
				synchronized (MyThread.class) {//这样3个线程才可以共享同一把锁	
					if (ticket>0) {
							//考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
							try {
								Thread.sleep(100);
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
							
							System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
						}
					}
			}else {
				sellTicket();	
			}
			x++;
		}
		
	}

//	private void sellTicket() {
//		synchronized (obj) {//这样3个线程才可以共享同一把锁	
//			if (ticket>0) {
//					//考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
//					try {
//						Thread.sleep(100);
//					} catch (InterruptedException e) {
//						// TODO Auto-generated catch block
//						e.printStackTrace();
//					}
//					
//					System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
//				}
//			}
//	}
	
	
	//同步方法:同步方法是将synchronized关键字加到方法上,同步方法的锁是this
//	private synchronized void sellTicket() {	
//		if (ticket>0) {
//				//考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
//				try {
//					Thread.sleep(100);
//				} catch (InterruptedException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//				
//				System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
//			
//		}
//	}
	
	
	//静态同步方法,他的锁是本类的字节码文件对象:类名.class。
	private static synchronized void sellTicket() {	
	if (ticket>0) {
			//考虑到实际的生活中,我们需要给每一个线程加入一定的延迟,模拟一下这种效果
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"张票");
		
	}
}

}


C:锁对象问题
a:同步代码块(定义一个抽象类,里面专门定义一个锁)
任意对象

b:同步方法(仅适用于实现runable接口)
public synchronized void sellTicket(){同步代码}
this

c:静态同步方法
类的字节码对象
public static synchronized void sellTicket() {
需要同步的代码
}


(8)匿名内部类的方式使用多线程(掌握)
new Thread() {
public void run() {
...
}
}.start();


new Thread(new Runnable(){
public void run() {
...
}
}).start();

案例:利用匿名内部类,启动多个线程,验证单例设计模式之懒汉式所存在的缺陷,

     当使用多线程来搞的时候就不单例了

package com.edu_11;

public class NiMingThread {
	public static void main(String[] args) {
		//方式1:
		new Thread(){
			//重写的方法
			@Override
			public void run() {
				for (int i = 0; i <10; i++) {
					System.out.println(i);
				}
			}
		}.start();
		
		//方式2
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i = 0; i <10; i++) {
					System.out.println(i);
				}
			}
		}).start();
		
		
	}

}



(9) JDK5的Lock锁,我们之前造的所有的锁都没有手动释放锁
static Lock lock = new ReentrantLock();
枷锁:lock.lock();
释放锁:lock.unlock();
可以让我们明确的知道在哪里加锁和释放锁。
依然写一个卖票的demo,用lock枷锁释放锁,
为了保证我们创建的锁一定会被释放,用一下代码进行改进
try{....}finally{.....}

(10)死锁问题
同步嵌套,锁里面套了一个锁,出现同步嵌套
(简单介绍,要求大家以后写的时候需要注意)

(11)线程等待和唤醒机制(案例演示:waitThread,NotifyThread,MyLock,Test)
锁对象调用wait()锁对象调用notify()
package com.edu_15;

public class WaitThread extends Thread{
	
	@Override
	public void run() {
		synchronized (MyLock.obj) {
			//让等待线程处于等待状态
			try {
				MyLock.obj.wait();//当线程处于等待状态的时候,线程就不会继续往下执行了
								  //线程在处于等待的时候,会释放掉自己手中的锁
								 //sleep()这个方法,在线程休息的时候会释放锁码?
								//答:不会
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("我被唤醒了");
	}

}

package com.edu_15;

public class Test {
	public static void main(String[] args) {
		//创建等待线程,让等待线程处于一个等待状态
		WaitThread wt = new WaitThread();
		wt.start();
		
		//睡上5秒钟之后唤醒等待线程
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		//创建唤醒线程对象
		NotifyThread nt = new NotifyThread();
		nt.start();
		
	}

}
package com.edu_15;

public class NotifyThread extends Thread{
	@Override
	public void run() {
		synchronized (MyLock.obj) {
			//幻想等待线程
			MyLock.obj.notify();//唤醒正在等待的线程,唤醒的等待线程的锁对象,必须和等待线程的锁对象一致
		}
	}

}
package com.edu_15;

public abstract class MyLock {
	public static final Object obj = new Object();

}




注意:
wait和sleep的区别
线程等待,在等待的同时释放锁,而sleep()方法在执行的过程中是不会释放锁的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值