java学习(15)

java学习(15)

这篇开始写关于多线程的内容。

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

1.2多线程的两种方案

方法一:继承Thread类,通过调用Thread类的start()方法,启动一个线程,

代码实现:

public class MyThreadTest extends Thread{

	@Override
	public void run() {
		System.out.println("这是子线程的run方法");
	}
}
public static void main(String[] args) {
		//创建MyThreadTest对象
		MyThreadTest mt = new MyThreadTest();
		
		//启动线程
		mt.start();
	}

方法二:实现Runable接口,再通过Thread类的构造方法,把实现Runable接口的类对象传进去,再调用start()方法,启动线程,代码实现:

public class MyThreadTest implements Runnable{

	@Override
	public void run() {
		System.out.println("这是子线程的run方法");
	}
}
public static void main(String[] args) {
		//创建MyThreadTest对象
		MyThreadTest mt = new MyThreadTest();
		
		//构造Thread对象
		Thread t = new Thread(mt);
		
		//启动线程
		t.start();
	}


1.3多线程的有关问题:

A:启动线程用的方法:start()

B:start()和run()的区别

start():

1.开启线程  2.执行run()方法里面的代码

run():执行的是线程里面执行的代码,并不会开启线程,简单来说,就是创建对象,调方法
C:重写run():因为需求不同
D:线程不可以多次启动


1.4线程的调度和控制

A.线程休眠:(Thread.sleep(毫秒值))--可以设置线程的休眠时间

Thread.sleep(500);//休眠500毫秒

B.线程名称:(setName(),getName();)--可以获取和设置线程的名称。对于实现了Runnable接口的类,调用Thread.currentThread().getName()来获取当前线程的名称。

//设置线程名称
mt1.setName("ss1");
mt2.setName("ss2");
mt3.setName("ss3");

C.线程的调度及优先级(抢占cpu执行权抢占到的概率):setPriority(10)(注意默认值是5,区间在1-10之间)--可以设置线程的优先级

注意:设置了优先级,只能保证优先级高的线程抢占到CPU的概率大,但不是一定能抢到!!

//线程的调度及优先级setPriority(10)(注意默认值是5,区间在1-10之间
mt1.setPriority(10);


1.5经典卖票案例

1.5.1继承Thread卖票

public class MyThread extends Thread{

	 int ticket = 20;
	@Override
	public void run() {
		while(true){
			//判断票数
			if(ticket>0){
				System.out.println(this.getName()+"正在卖"+ticket--+"张票");
			}
		}
	}
	
}
public static void main(String[] args) {
		//创建mythread
		MyThread mt1 = new MyThread();
		MyThread mt2 = new MyThread();
		MyThread mt3 = new MyThread();
		
		//线程名称(setName(),getName();)
		mt1.setName("窗口一");
		mt2.setName("窗口二");
		mt3.setName("窗口三");
		
		//启动线m
		mt1.start();
		mt2.start();
		mt3.start();
	}


1.5.2实现Runnable卖票

public class MyThread implements Runnable{

	int ticket = 20;
	@Override
	public void run() {
		while(true){
			//判断票数
			if(ticket>0){
				System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");
			}
		}
	}
	
}
public static void main(String[] args) {
		//创建mythread
		MyThread mt = new MyThread();
		
		//创建Thread对象
		Thread t1 = new Thread(mt);
		Thread t2 = new Thread(mt);
		Thread t3 = new Thread(mt);
		
		//线程名称(setName(),getName();)
		t1.setName("窗口一");
		t2.setName("窗口二");
		t3.setName("窗口三");
		
		//启动线m
		t1.start();
		t2.start();
		t3.start();
	}

分析:按照真实的情景加入了延迟后,发现出现了这样的两个问题:

  A:相同的票卖了多次


  B:出现了负数的票


原因:(1)CPU的一次操作必须是原子性的(操作是CPU执行一次就可以直接完成的)

  (2)线程抢占随机性和延迟导致的


1.6多线程安全问题分析:

A:是否是多线程环境
B:是否有共享数据
C:是否有多条语句操作共享数据

1.7解决多线程安全问题:线程安全执行效率就低,这里对于各种方法,对上述代码进行部分改写。
(1)同步代码块(测试不是同一个锁的情况,测试是同一个锁的情况)
synchronized(对象) {
需要被同步的代码。
}
分析:这里的对象是一个任意对象 ,相当于是一把锁,只要线程进去就把锁锁上:

需要同步的代码就是被线程执行的代码。对象是同一个的时候,线程安全:对象不是同一个的时候,线程不安全。上述代码块可以这么改写,这样就不会出现线程安全问题:

public class MyThread implements Runnable{
	
	int ticket = 100;
	//创建锁对象
	Object obj = new Object();
	@Override
	public void run() {
		while(true){
			synchronized(obj){
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//判断票数
				if(ticket>0){
					System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");
				}
			}
			
		}
	}
	
}

(2)锁对象问题

a:同步代码块(定义一个抽象类,里面专门定义一个锁)
public abstract class MyLock {
	//定义锁
	public static final Object obj = new Object();	
}

b:同步方法(仅适用于实现runable接口)锁是:this

public synchronized void sellTicket(){

同步代码

}

//同步方法:同步方法是将synchronized关键字加到方法上,
//同步方法的锁是this
	private synchronized void sellTicket() {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//判断票数
			if(ticket>0){
				System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");
			}
	}
c:静态同步方法 锁是:类的字节码对象
public static synchronized void sellTicket() {
需要同步的代码

}

//静态同步方法,他的锁是本类的字节码文件对象:类名.class
	public static synchronized void sellTicket(){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//判断票数
			if(ticket>0){
				System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");
			}
	}




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


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

}).start();

示例:

//方法一
		new Thread(){
			@Override
			public void run() {
				System.out.println("方法一的线程");
			}
		}.start();
		
		//方法二
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				System.out.println("方法二的线程");
			}
		}).start();

1.9 JDK5的Lock锁:我们之前造的所有的锁都没有手动释放锁
static Lock lock = new ReentrantLock();
加锁:lock.lock();

释放锁:lock.unlock();


对上面代码改写:

public class MyThread implements Runnable{

	int ticket = 100;
	
	//static Lock lock = new ReentrantLock();
	static Lock lock = new ReentrantLock();
	
	
	@Override
	public void run() {
		while(true){
			try {
				//加锁:lock.lock();
				lock.lock();
				//判断票数
				if(ticket>0){
					System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--+"张票");
				}
			}finally{
				//释放锁:lock.unlock();
				lock.unlock();
			}
		}
	}
}

1.10死锁问题:同步嵌套,锁里面套了一个锁,出现同步嵌套

示例:

public class MyLock {
	//定义两个锁
	public static final Object objA = new Object();
	public static final Object objB = new Object();
}

public class Deadlock extends Thread{

	boolean flag;
	
	//有参的构造方法
	public Deadlock(boolean flag){
		this.flag = flag;
	};
	
	//run方法
	@Override
	public void run() {
		if(flag){
			synchronized(MyLock.objA){
				System.out.println("if"+"MyLock.objA锁");
				synchronized(MyLock.objB){
					System.out.println("if"+"MyLock.objB锁");
				}
			}
		}else{
			synchronized(MyLock.objB){
				System.out.println("else"+"MyLock.objB锁");
				synchronized(MyLock.objA){
					System.out.println("else"+"MyLock.objA锁");
				}
			}
		}
	}
}
1.11线程等待和唤醒机制

锁对象调用wait()

唤醒对象调用notify()

示例:

public abstract class MyLock {
	//定义锁
	public static final Object obj = new Object();	
}
public class NotifyThread extends Thread{

	@Override
	public void run() {
		//唤醒等待线程
		synchronized(MyLock.obj){
			MyLock.obj.notify();
		}	
	}
	
}
public class WaitThread extends Thread{

	@Override
	public void run() {
		synchronized(MyLock.obj){
			try {
				MyLock.obj.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(this.getName()+"唤醒了");
	}
}
public static void main(String[] args) {
		//创建线程对象
		WaitThread wt = new WaitThread();
		
		//设置名称
		wt.setName("awakeThread");
		
		//让进程等待
		wt.start();
		
		//2秒后唤醒
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//唤醒对象调用notify()
		//创建唤醒线程对象
		NotifyThread nt = new NotifyThread();
		nt.start();
	}
运行:


注意:wait()线程等待,在等待的同时释放锁,而sleep()方法在执行的过程中是不会释放锁的
发布了115 篇原创文章 · 获赞 72 · 访问量 9万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览