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

java-多线程1

2015-09-14 20:54 821 查看
进程概念:

进程就是一个正在执行的程序,每一个进程执行都有一个执行的顺序,该顺序是一个执行的路径,或者叫过一个控制单元。

线程就是进程中的一个独立的控制单元,线程在控制着进程的执行。一个进程至少有一个线程。

一、自定义一个线程:

java中已经提供了对线程的描述,就是Thread类。

方法一:

1,继承thread类,并且重写run方法。

2,调用线程的start方法

如:

<span style="font-size:14px;">class Demo extends Thread
{
public void run()//重新run方法
{
for(int x=0;x<100;x++)
{
System.out.println("run start");
}
}
}
class ThreadDemo
{
public stitac void main(String[] args)
{
Demo d = new Demo();
//开启线程的时候,该线程会自动调用run方法。
d.start();
}
}</span>
注意:当我们使用d.run();这样的方式调用run方法也是可以的,但是这样就是主线程在执行,而新建立的线程并没有执行。

方法二:

就是声明线程实例类,实现Runnable接口,并覆盖Runnable中的run方法。但是要要注意的是,创建线程还是只能通过继承

Thread类,或者是直接new Thread类来创建线程对象。

步骤:

1,定义类实现Runnable接口

2,覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。

3,通过Thread类建立线程对象。

4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。

5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

实现方式和继承方式有什么区别呢?

1,实现方式好处:避免了单继承的局限性。

2,在定义线程时,建立使用实现方式。

两种方式区别:

1,继承Thread:线程代码存放Thread子类run方法中。

2,实现Runnable,线程代码存在接口的子类的run方法。

小例子:

<span style="font-size:14px;">class Ticket implements Runnable
{
private tick=100;
public run()
{
while(true)
{
if(tick>0)
System.out.println("num:"+tick--);
}
}
}
class ThreadDemo
{
public stitac void main(String[] args)
{
//这里要注意,new Ticket()并非创建一个线程,因为Ticket类并没有继承Thread。
Ticket tic = new Ticket();
//这里才是创建线程,并把所要操作的对象tic传入。
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
t2.start();;
}
}</span>
二、线程的状态

1,start(),该状态是启动线程的,当创建线程后,使用start()方法就可以启动线程了。

2,sleep(),wait():这两种就是让进程进入冻结状态,说白了就是暂停,notify()可以换醒,

3,stop(),消亡状态

4,还有一种特殊的状态,那就是阻塞状态,这中状态是自然存在的,由于CPU同一时间只能执行一个线程,所以当某一线程具有执行权的时候,其它线程必须等待。

三、多线程的安全性

这是多线程使用过程中最常出现的问题,当多个线程操作共享数据的时,由于CPU的特性,经常会出现数据错乱的情况,就像上述例子中,虽然每次打印tick都有判断tick>0,但是当线程一判断完了tick>0之后便处于阻塞状态了,这时线程二又对tick进行判断,当tick=1的时候就会出现打印出tick为负数的情况了。解决的方法就是给操作共享数据的代码加锁,这样在锁内的代码块就叫做同步代码快,里面的代码在同一时间内只能被一条线程执行!如下例子:

<span style="font-size:14px;">class Ticket implements Runnable
{
private tick=100;
Object obj = new Object();
public run()
{
while(true)
{
//这里添加锁
synchronized(obj)
{
//同步代码块
if(tick>0)
System.out.println("num:"+tick--);
}
}
}
}
class ThreadDemo
{
public stitac void main(String[] args)
{
//这里要注意,new Ticket()并非创建一个线程,因为Ticket类并没有继承Thread。
Ticket tic = new Ticket();
//这里才是创建线程,并把所要操作的对象tic传入。
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
t2.start();;
}
}</span>
还有个比较特殊的地方也经常会出现安全性的问题,那就是在单例设计模式的时候(懒汉式),同样需要加锁去解决,但是会降低性能,我们通过把代码改成注释的部分,加入双重判断,这样就能够减少线程对锁的判断,稍微提高一些程序的性能。

如下代码:

<span style="font-size:14px;">class Single
{
private static Single single = null;
private Single(){}
public static Single getInstance()
{
//此处如果不加锁也会出现安全性的问题
synchronized(Single.class)
{
if(single==null)
single = new Single();
return single;
}
/*
此处加入了双重判断,是为了提高效率
if(single==null)
{
synchronized(Single.class)
{
if(single==null)
single = new Single();
return single;
}
}
*/
}
}</span>
使用同步的前提:

1,必须是多个线程,并要操作共享数据。

2,必须使用同一个监视器(锁)。

注意:同步虽然是解决了安全性的问题,但是也降低了程序的性能,因为线程每次都要去判断锁。

同步函数:

1,当我没要运行的的功能函数里面的代码都需要进行同不步的时候,我们可以使用synchronizd关键字修饰函数,使函数具有同步的特性。

2,同步函数使用的监视器(锁)是this关键字,也就是当前对象,但是要注意的一点就是当该函数是静态的,并且被synchronized修饰了,那么这时候的锁就不在是this了,而是函数所在类的字节码文件,类名.class。

死锁:

同步还有个小问题,那就是死锁,当同步代码块中嵌套了同步代码块的时候就很容易出现死锁了。

看下面的例子:
<span style="font-size:14px;">class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLock.locka)//假如t1线程拿到A锁
{
System.out.println(Thread.currentThread().getName()+"...if locka ");
//B锁在线程t2中所以t1线程只能等待
synchronized(MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"..if

lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockb)//假如t2线程拿到B锁
{
System.out.println(Thread.currentThread().getName()+"..else lockb");
//B锁在线程t1中所以t2线程只能等待
synchronized(MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+".....else

locka");
}
}
}
}
}
}
class MyLock
{
//这个类仅仅是为了比较方便的创建两个锁而已。
static Object locka = new Object();
static Object lockb = new Object();
}
class  DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}</span>
上述这个列子就是出现死锁的表现,在实际开发中应该尽量避免这种情况的出现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: