JavaSe基础XX15——线程_2

*08-多线程(线程的状态)

CPU的执行资格:可以被CPU处理但在处理队列排列

CPU的执行权:正在被CPU执行


*09-多线程(创建线程的第二种方式—实现Runnable接口)

类,类中的代码不止一个人要用。而且需要同时执行。这个时候就需要线程来实现。

package testThread;

public class main {

	public static void main(String[] args) {
		myThread t1 = new myThread();
		myThread t2 = new myThread();
		t1.start();
		t2.start();
	}
}

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

但假如myThread有父类,就不能再继承Thread了。那该怎么办?

当一个类有父类的时候就不能再继承Thread的了。

不继承也有其他方法解决。——扩展Demo类的内容,实现多功能,接口。implement









class Demo implements Runnable//extends Fu //准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。
					//通过接口的形式完成。
{
	public void run()
	{
		show();
	}
	public void show()
	{
		for(int x=0; x<20; x++)
		{
			System.out.println(Thread.currentThread().getName()+"....."+x);
		}
	}
}


class  ThreadDemo
{
	public static void main(String[] args) 
	{	
		Demo d = new Demo();
		Thread t1 = new Thread(d);
		Thread t2 = new Thread(d);
		t1.start();
		t2.start();


//		Demo d1 = new Demo();<span style="white-space:pre">	</span>d1不是线程对象
//		Demo d2 = new Demo();
//		d1.start();
//		d2.start();
	}
}


/*
创建线程的第一种方式:继承Thread类。

创建线程的第二种方式:实现Runnable接口。

1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
	为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
	所以要在线程对象创建时就必须明确要运行的任务。

4,调用线程对象的start方法开启线程。


实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
	按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。

所以,创建线程的第二种方式较为常用。

*/

*10-多线程(第二种方式的细节)

内部的设计思想


class Thread 
{
	private Runnable r;
	Thread()
	{
	
	}
	Thread(Runnable r)
	{
		this.r  = r;
	}

	public void run()
	{
		if(r!=null)
			r.run();
	}

	public void start()
	{
		run();
	}
}
class ThreadImpl implements Runnable
{
	public void run()
	{
		System.out.println("runnable run");
	}
}
ThreadImpl i = new ThreadImpl();
Thread t = new Thread(i);
t.start();




class SubThread extends Thread
{
	public void run()
	{
		System.out.println("hahah");
	}
}
//SubThread s = new SubThread();
//s.start();


*11-多线程(第二种方式的好处)



实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。

所以,创建线程的第二种方式较为常用。


*12-多线程(卖票示例)

写程序的步骤:

1)写需求

2)。。。

。。。。








package testThread;

class Ticket implements Runnable {
	private int num = 100;

	@Override
	public void run() {
		while (num > 0) {
			System.out
					.println(Thread.currentThread().getName() + "..." + num--);
		}
	}
}

public class TicketDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		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();
	}

}


*13-多线程(线程安全问题的现象)

我们分析上面这个程序的时候可以发现,存在一些情况。

package testThread;

class Ticket implements Runnable {
	private int num = 10;

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

public class TicketDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		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();
	}

}

运行的结果当中出现了这样的情况:

Thread-1...10
Thread-2...9
Thread-0...8
Thread-2...7
Thread-1...6
Thread-3...5
Thread-2...4
Thread-1...3
Thread-2...2
Thread-3...1
Thread-2...0
Thread-1...-1
Thread-0...-2



或者用thread.sleep(10)来进行停顿,看效果。


如何避免呢?


*14-多线程(线程安全问题产生的原因)

线程安全问题产生的原因:

1,多个线程在操作共享的数据。
2,操作共享数据的线程代码有多条


*15-多线程(同步代码块)


解决思路;
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,
其他线程时不可以参与运算的。
必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。 


在java中,用同步代码块就可以解决这个问题。


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

同步的好处:解决了线程的安全问题。

同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。

同步的前提:同步中必须有多个线程并使用同一个锁。


class Ticket implements Runnable//extends Thread
{
	private  int num = 100;

	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			synchronized(obj)
			{
				if(num>0)
				{
					try{Thread.sleep(10);}catch (InterruptedException e){}
					
					System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
				}
			}
		}
	}
}

完整代码:

package testThread;

class Ticket implements Runnable {
	private int num = 1000;

	Object obj = new Object();

	@Override
	public void run() {

		while (num > 0) {
			synchronized (obj) {
				// try {
				// Thread.sleep(20);
				// } catch (InterruptedException e) {
				// e.printStackTrace();
				// }
				System.out.println(Thread.currentThread().getName() + "..."
						+ num--);
			}
		}
	}
}

public class TicketDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		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();
	}

}


*16-多线程(同步的好处和弊端)

加了synchronized 关键字之后,为什么就能保证其他线程进不来了呢?

同步的好处:解决了线程的安全问题。

同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。效率低是在我们可承受范围之内。

同步的前提:同步中必须有多个线程并使用同一个锁。




*17-多线程(同步的前提)

同步的前提:同步中必须有多个线程并使用同一个锁。



*18-多线程(同步函数)

银行存款的例子。

/*
需求:储户,两个,每个都到银行存钱每次存100,,共存三次。
*/

class Bank
{
	private int sum;
//	private Object obj = new Object();
	public synchronized void add(int num)//同步函数
	{
//		synchronized(obj)
//		{
			sum = sum + num;
	//			-->
			try{Thread.sleep(10);}catch(InterruptedException e){}
			System.out.println("sum="+sum);
//		}
	}
}


class Cus implements Runnable
{
	private Bank b = new Bank();
	public void run()
	{
		for(int x=0; x<3; x++)
		{
			b.add(100);
		}
	}
}


class BankDemo 
{
	public static void main(String[] args) 
	{
		Cus c = new Cus();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();
	}
}



同步代码块——用的锁是我们自己建立的obj

同步函数——的锁是什么呢?

都可以保证同步。

---------------------------------------

package testThread;

class Bank {
	private int sum;

	public void add(int num) {
		sum = num + sum;
		System.out.println("-->sum:" + sum);
	}
}

class Person implements Runnable {
	Bank b = new Bank();

	@Override
	public void run() {
		for (int i = 0; i < 3; i++) {
			b.add(100);
		}
	}
}

public class BankDemo {

	public static void main(String[] args) {
		Person p = new Person();
		Thread t1 = new Thread(p);
		Thread t2 = new Thread(p);
		t1.start();
		t2.start();

	}
}

上面的代码会出现线程不安全的情况,结果的输出为:

-->sum:200
-->sum:200
-->sum:400
-->sum:500
-->sum:300
-->sum:600

方法1:

package testThread;

class Bank {
	private int sum;
	private Object obj = new Object();

	public void add(int num) {
		// 方法一,在下面的语句添加syncni关键字
		synchronized (obj) {
			sum = num + sum;
			System.out.println("-->sum:" + sum);
		}
	}
}

class Person implements Runnable {
	Bank b = new Bank();

	@Override
	public void run() {
		for (int i = 0; i < 3; i++) {
			b.add(100);
		}
	}
}

public class BankDemo {

	public static void main(String[] args) {
		Person p = new Person();
		Thread t1 = new Thread(p);
		Thread t2 = new Thread(p);
		t1.start();
		t2.start();

	}
}

方法2:

package testThread;

class Bank {
	private int sum;

	public synchronized void add(int num) {// 方法二,同步函数
		sum = num + sum;
		System.out.println("-->sum:" + sum);
	}
}

class Person implements Runnable {
	Bank b = new Bank();

	@Override
	public void run() {
		for (int i = 0; i < 3; i++) {
			b.add(100);
		}
	}
}

public class BankDemo {

	public static void main(String[] args) {
		Person p = new Person();
		Thread t1 = new Thread(p);
		Thread t2 = new Thread(p);
		t1.start();
		t2.start();

	}
}


*19-多线程(验证同步函数的锁)

验证同步代码块和同步函数使用的锁是不是同一个。

同步函数的锁是this。

同步函数的使用的锁是this;


同步函数和同步代码块的区别:
同步函数的锁是固定的this。
同步代码块的锁是任意的对象。
建议使用同步代码块。

举例:


package testThread;

class Ticket implements Runnable {
	private int num = 100;
	boolean flag = true;
	Object obj = new Object();

	@Override
	public void run() {
		if (flag)
			while (true) {
				synchronized (obj) {
					if (num > 0) {
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()
								+ "...obj..." + num--);
					}
				}
			}
		else
			while (true) {
				show();
			}

	}

	public synchronized void show() {
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		if (num > 0)
			System.out.println(Thread.currentThread().getName() + "...func..."
					+ num--);
	}
}

public class TicketDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Ticket t = new Ticket();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);

		t1.start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		t.flag = false;
		t2.start();
	}

}

上面的代码会有着两种结果输出:


结果就说明了两个不是同一个锁。

修改:

if (flag)
			while (true) {
				synchronized (this) {
					if (num > 0) {
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()
								+ "...obj..." + num--);
					}
				}
			}
		else
			while (true) {
				show();
			}

*20-多线程(验证静态同步函数的锁)

把num的值改为statci。


输出的结果不正确,说明了静态同步函数的锁不是this,原因很简单,因为静态函数没有this。

/*
静态的同步函数使用的锁是  该函数所属字节码文件对象 
可以用 getClass方法获取,也可以用当前  类名.class 表示。


*/

class Ticket implements Runnable
{
	private static  int num = 100;
//	Object obj = new Object();
	boolean flag = true;
	public void run()
	{
//		System.out.println("this:"+this.getClass());

		if(flag)
			while(true)
			{
				synchronized(Ticket.class)//(this.getClass())
				{
					if(num>0)
					{
						try{Thread.sleep(10);}catch (InterruptedException e){}						
						System.out.println(Thread.currentThread().getName()+".....obj...."+num--);
					}
				}
			}
		else
			while(true)
				this.show();
	}

	public static synchronized void show()
	{
		if(num>0)
		{
			try{Thread.sleep(10);}catch (InterruptedException e){}
			
			System.out.println(Thread.currentThread().getName()+".....function...."+num--);
		}
	}
}

class StaticSynFunctionLockDemo 
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();

//		Class clazz = t.getClass();
//		
//		Class clazz = Ticket.class;
//		System.out.println("t:"+t.getClass());

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);

		t1.start();
		try{Thread.sleep(10);}catch(InterruptedException e){}
		t.flag = false;
		t2.start();
	}
}

总结:

静态的同步函数使用的锁是  该函数所属字节码文件对象 
可以用 getClass方法获取,也可以用当前  类名.class 表示。


*21-多线程(单例模式涉及的多线程问题)

/*
多线程下的单例

*/

//饿汉式
class Single
{
	private static final Single s = new Single();
	private Single(){}
	public static Single getInstance()
	{
		return s;
	}
}


class Single
{
	private static Single s = null;

	private Single(){}

	public static Single getInstance()
	{
		if(s==null)
		{
			synchronized(Single.class)		
			{
				if(s==null)
		//				-->0 -->1
					s = new Single();
			}
		}
		return s;
	}
}


在多线情况下,有没有安全隐患。

饿汉式没有,懒汉式有。

解决方式1;


但这样的话,就会降低效率。因为假如想取得一个实例,首先要判断锁,之后在判断是否为空。这个时候,我们可以对代码进行改写。

getClass是非静态的的。不能用做锁。


所以可以用这种双重判断来解决单例模式的效率和安全问题。


*22-多线程(死锁示例)

死锁的体现形式之一 —— 同步的嵌套。

什么是死锁,看下面的例子、

/*
死锁:常见情景之一:同步的嵌套。

*/
class Ticket implements Runnable
{
	private  int num = 100;
	Object obj = new Object();
	boolean flag = true;
	public void run()
	{


		if(flag)
			while(true)
			{
				synchronized(obj)
				{
					show();
				}
			}
		else
			while(true)
				this.show();
	}

	public synchronized void show()
	{

		synchronized(obj)
		{
			if(num>0)
			{
				try{Thread.sleep(10);}catch (InterruptedException e){}
				
				System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
			}
		}
	}
}


class DeadLockDemo 
{
	public static void main(String[] args) 
	{
		Ticket t = new Ticket();
//		System.out.println("t:"+t);

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);

		t1.start();
		try{Thread.sleep(10);}catch(InterruptedException e){}
		t.flag = false;
		t2.start();
	}
}




面试:请写出一个死锁程序。

class Test implements Runnable
{
	private boolean flag;
	Test(boolean flag)
	{
		this.flag = flag;
	}

	public void run()
	{
		
		if(flag)
		{
			while(true)
				synchronized(MyLock.locka)
				{
					System.out.println(Thread.currentThread().getName()+"..if   locka....");
					synchronized(MyLock.lockb)				{
						
						System.out.println(Thread.currentThread().getName()+"..if   lockb....");
					}
				}
		}
		else
		{
			while(true)			
				synchronized(MyLock.lockb)
				{
					System.out.println(Thread.currentThread().getName()+"..else  lockb....");
					synchronized(MyLock.locka)
					{
						System.out.println(Thread.currentThread().getName()+"..else   locka....");
					}
				}
		}

	}

}

class MyLock
{
	public static final Object locka = new Object();
	public static final Object lockb = new Object();
}




class DeadLockTest 
{
	public static void main(String[] args) 
	{
		Test a = new Test(true);
		Test b = new Test(false);

		Thread t1 = new Thread(a);
		Thread t2 = new Thread(b);
		t1.start();
		t2.start();
	}
}


*郁闷答疑——关于Obj放的位置



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值