[Java]多线程复习(更新未完)

Posted 晚起的男孩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Java]多线程复习(更新未完)相关的知识,希望对你有一定的参考价值。

知识点小结:(具体看例子)

多线程:

线程是程序中单独创建的控制单元,是并发执行的程序。外部顺序执行的程序叫做主线程
线程是多任务操作系统调用CPU来回切换的程序。
注意:线程开启要用start方法,虚拟机调用底层向操作系统申请一个单独线程。如果你只执行run方法,
那么并没有开启一个线程,仅仅是一个普通类的方法,那么是在主线程中顺序执行的。你开启start是虚
拟机新开启一个线程后自动去调用run方法的。
Windows执行虚拟机,虚拟机执行多线程程序,多线程程序调用底层Windows,最终是Windows调用自己。

线程五种状态:
创建,消亡:stop方法或run方法执行完
临时状态:阻塞。有执行资格但没有执行权。
sleep,wait是冻结状态,放弃执行资格。
notify后,回到临时状态,不一定运行,因为不一定抢到执行权。

因为我们的Thread继承Thread父类,要用super调用父类构造方法赋予名称

卖票:多线程共享同样的资源,定义为静态。ticket--,1比2,3号先打印出来是双核CPU造成的,因为调用
在命令行打印也是一个程序。如果不定义为静态,Ticket仍然继承Thread复写run方法,那么只能有一个
窗口卖票。所以要用另外的方式创建多线程执行的对象。
让Thread调用实现Runnable的类的run方法。

实现方式和继承方式的区别:(面-试-考-点)java单根继承结构,已有继承的类无法再继承Thread

安全问题:多线程执行同一段代码操作同一资源时。
这里的卖票例子,到最后一张票时,可能某个线程挂
起在tickets--之前,另一个线程进来,输出后tickets--,这时票已经为0了,等待的线程再执行,出现0
,-1,-2号票等情况

产生的原因:多条语句操作共享数据,一个线程对多条语句只执行了一部分,另一个进程参与进来导致共
享数据错误。

解决:对多条操作共享数据的语句同步起来(同步代码块,锁是一个运行期间唯一对象就行),判断该对
象状态,进入,锁状态,执行,直到该线程执行完,解锁,其他线程才进来。锁,锁旗标,监视器。

同步前提:1.两个或两个以上线程 2.必须是多个线程使用同一个锁(执行同段同步代码)

run方法要是整个放在同步里,成了单线程了。不需要同步的代码根本不需要放在同步里。

同步函数的锁是this
证明:同时写一个同步函数和一个以this为锁的同步代码块,内容是操作同一个资源,两个线程各自跑
注意,如果同步代码块用的不是this这个锁,由于不是同一个锁,那么两个线程就没有实现同步,各运行

各的,操作的是多条语句的同一资源,那么就会有线程安全问题。

静态同步函数的锁是类字节码文件对象
验证:把上面例子同步代码块的锁改成此字节码文件对象就会确保线程安全,而不是就会有线程安全问题

死锁:同步代码块嵌套,用的是同样的锁的时候,两个线程各自需要对方的锁。


几个例子:

1.卖票:已解决线程安全问题(多线程操作多条语句的同一资源)

package cn.xbai.thread;


class Ticket implements Runnable{
	private int tickets=100;
	
	public void run(){
		while(true){
			synchronized(this){
			if(tickets<=0)//一定要自己尝试,看效果决定知识点理解的对不对,方案能不能解决问题!!
				break;
			//0------1---------2---------3
			//synchronized(this){//放这里也不对!!可能都挂起在上面的判断语句那里!!尤其是这里加了同步,一个线程锁住了,其他线程进不来更容易挂起在这里!!在最后1张票卖完后再进入,出现0,-1,-2!!!
			//自己验证:只同步最下面一句是解决不了问题的,仍然可以都挂起在sleep语句那里,所以要把操作同一资源的多-条-语-句都同步起来,因为可能一个线程只执行部分语句,执行权就被夺走了,引发操作同一资源的线程安全问题。sleep语句只是为了演示这种多语句操作同一资源时执行权被夺走的情况(放大这种被夺走的概率,或者说手动演示就让它被夺走!)
			try {//剩1张票时,都过了上面的判断条件到这里------->我这里出现了更严重的情况:(老师的是判断tickets>0时执行)因为几个线程到了这里都唤醒,tickets变为负数,那么再次进入循环时发现不是0了,永不结束了!!为了便于说明改成<=0!!
				//0------1-------2--------3
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//synchronized(this){
			System.out.println(Thread.currentThread().getName()+"..."+tickets--);
			//}
			}
		}
	}
}


public class TicketDemo {
	
	public static void main(String[] args){
		
		Ticket ticket=new Ticket();
		new Thread(ticket).start();
		new Thread(ticket).start();
		new Thread(ticket).start();
		new Thread(ticket).start();
		new Thread(ticket).start();
	}
}

输出结果:(部分示例)

Thread-0...100
Thread-2...99
Thread-4...98
Thread-1...97
Thread-3...96
Thread-0...95

...

Thread-0...25
Thread-2...24
Thread-4...23
Thread-1...22
Thread-3...21
Thread-0...20
Thread-2...19
Thread-4...18
Thread-1...17
Thread-3...16
Thread-0...15
Thread-2...14
Thread-4...13
Thread-1...12
Thread-3...11
Thread-0...10
Thread-2...9
Thread-4...8
Thread-1...7
Thread-3...6
Thread-0...5
Thread-2...4
Thread-4...3
Thread-1...2
Thread-3...1

2.银行存钱:已解决线程安全问题

package cn.xbai.thread;

class Bank{
	private int sum=0;
	
	public int add(int num){
		return sum+=num;
	}
}

class Cus implements Runnable{
	
	private Bank bank=new Bank();
	public void run(){
		//循环多次才能看出多线程任务的意义
		for(int i=0;i<3;i++){//i不需要同步,局部变量,每个线程都有一个
			synchronized(this){
			//放慢过程,放大矛盾,这样问题就产生了
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}//我不明白这里怎么会出现输出两个100的情况,下面应该是两条语句:
			System.out.println(bank.add(100));
			}
			/**
100
100         //这里返回值应该是一个独立结果中间值,是sum的副本值,并且应该是先输出后加的,那么应该是第一个线程想输出还没加,第二个线程进来输出并加了,第一个线程直接输出100,然后再加?
200
300
400
500
500
600
700
800
900
1000
1100
1200
1300

			 */
			/**
200
100
300
500
400
600             这一组执行结果会提供原因吗?
700
800
900
1000
1100
1200
1300
1400
1500

			 */
		}
	}
}
public class BankDemo {
	
	public static void main(String[] args){
		
		Cus c=new Cus();
		new Thread(c).start();//每个人存300
		new Thread(c).start();
		new Thread(c).start();
		new Thread(c).start();
		new Thread(c).start();
	}
}

运行结果:

100
200
300
400
500
600
700
800
900
1000
1100
1200
1300
1400
1500

3.单例设计模式的懒汉式的线程安全问题:面试题多个考点(为什么会出现线程安全问题,怎么解决其线程安全问题,用什么锁,怎么提高同步后的效率...)!!

package cn.xbai.thread;
//单例设计模式:饿汉式--------->没有线程安全问题:操作共享资源的语句只有一句
/*class Single{
	private static final Single s=new Single();//final代表不可变,static代表只有一份(还有不依赖对象,但这里本来就只有一个对象)
	private Single(){}
	public static Single getInstance(){
		return s;
	}
}*/
//懒汉式:延迟加载
class Single{
	private static Single s=null;//注意这里不能final了,否则无法再赋值
	private Single(){}
	public static Single getInstance(){
		//加上双重判断,提高懒汉式同步的效率,只要创建了对象,外层if不满足,就不用再进入同步代码块(判断锁的过程是低效的)
		if (s == null) {
			synchronized (Single.class) {// 注意静态方法是没有this的,锁是字节码文件对象!!
				if (s == null) {// 注意要判断!不为空不用再创建!
					// -------A---------B
					s = new Single();// s为共享数据,懒汉式在多线程访问时会出现安全隐患:单列模式new出了多个对象!!需要同步!!
				}
			}
		}
		return s;
	}
}
public class SingleDemo {
	
	
}

4.死锁:死锁出现的原因,写一个死锁程序,怎样避免死锁,面试考点!!

package cn.xbai.thread;

class DeadPool implements Runnable{
	
	public boolean flag=true;
	
	public void run(){
		if(flag==true){
			while(true){
			synchronized(Locks.LOCK_ONE){
				System.out.println("if lock one");
				synchronized(Locks.LOCK_TWO){
					System.out.println("if lock two");
				}
			}
			}
		}else{
			while(true){
			synchronized(Locks.LOCK_TWO){
				System.out.println("else lock two");
				synchronized(Locks.LOCK_ONE){
					System.out.println("else lock one");
				}
			}
			}
		}
	}
}

class Locks{
	public static final Object LOCK_ONE=new Object();
	public static final Object LOCK_TWO=new Object();
}
//同步代码块嵌套,锁不同,两线程各自需要对方占用的锁,造成死锁
public class DeadLockDemo {
	
	public static void main(String[] args){
		
		DeadPool deadpool=new DeadPool();
		new Thread(deadpool).start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		deadpool.flag=false;
		new Thread(deadpool).start();//只要用同样两个锁,即使两个线程执行的是不同的实现Runnable的对象也会出现这个死锁问题!这和多线程操作同一个资源没有关系!!
	}
}//下翻看下面的执行结果和分析:
/**
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
else lock two-----------------------结果:else锁住lock two,if锁住lock one,各自再也进不去内层,也都没结束,死锁!
if lock one


 * @author Administrator
 *
 */


以上是关于[Java]多线程复习(更新未完)的主要内容,如果未能解决你的问题,请参考以下文章

[Java复习05] 多线程&并发 知识点补充

Java多线程_复习

你需要了解的多线程知识(JAVA ) 复习

40个Java多线程问题详解复习

Java 多线程优先级休眠(未完待续...)

Java复习——多线程与并发库