您的位置:首页 > 编程语言 > Java开发

JavaSe基础XX15——线程_2

2014-08-25 10:12 337 查看
*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放的位置
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: