java线程基础,多线程的基础实现
-
多线程基础api,线程的实现类、线程的启动、暂停、停止、优先级、线程安全
java中线程的实现有两种方式,第一种是实现Runnable 接口,第二种是继承Thread类,二者工作时的性质一样的,并没有实质上的区别。都是java多态的一种实现,只是Thread在设计中不支持多继承,这是由于java本身只允许单根继承有关。
代码片段:
/**
*
* @author wodezuiaishinageren
* java 继承Thread类
*/
public class JavaThreadExtendsThread extends Thread {
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+"继承:"+JavaThreadExtendsThread.class.getSuperclass());
}
}
运行结果: Thread-0继承:class java.lang.Thread
/**
*
* @author wodezuiaishinageren
* 实现Runnable接口
*/
public class JavaThreadImplementsRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"实现:"+JavaThreadImplementsRunnable.class.getSuperclass());
}
}
运行结果: Thread-1实现:class java.lang.Object(因为superClass)
测试代码:
public static void main(String[] args) {
JavaThreadExtendsThread javaThreadExtendsThread=new JavaThreadExtendsThread();
simpleExtendsThreadRun(javaThreadExtendsThread);
Runnable javaThreadImplementsRunnable=new JavaThreadImplementsRunnable();
simpleImplementsRunnable(javaThreadImplementsRunnable);
System.out.println("运行结束!!!");
}
输出结果: Thread-0继承:class java.lang.Thread
运行结束!!!
Thread-1实现:[Ljava.lang.Class;@10b58352
从测试的运行结果中可可以得知,线程的Run方法在运行的时候输出的结果未按照代码的先后的书写顺序机型输出,说明,多线程技术在运行时,与代码的执行顺序或者调用顺序无关。线程是一个子任务,cpu以不确定的方式,或者说随机的时间来调用线程中的run方法,所以会出现上述输出的情况。⚠:如果多次调用start()方法,则会出现异常 Exception in thread ,java.lang.IllegalThreadStateException。如果调用的是thread.run()方法则,线程的执行是同步的,而非异步执行,此时线程对象不交给"线程规划器”来进行处理。线程的启动顺序跟书写线程的start()方法无关。
-
3.2.2线程中的实例变量与线程安全
在自定义的线程中的实例变量针对其它的线程可以有共享和不共享之分。
第一种不共享数据的情况:
代码片段:
/**
*
* @author wodezuiaishinageren
* 不共享数据
*/
public class ThreadDontShareData extends Thread {
private int count=5;
public ThreadDontShareData(String name) {
super();
this.setName(name);//设置线程名称
}
@Override
public void run() {
super.run();
while(count>0){
count--;
System.out.println("现在是:"+this.currentThread().getName()+"计算count="+count);
}
}
}
/**
*
* @author wodezuiaishinageren
* 线程是否共享数据测试类
*
*/
public class JavaThreadDataTest {
public static void main(String[] args) {
ThreadDontShareData dontShareData=new ThreadDontShareData("A");
ThreadDontShareData dontShareData2=new ThreadDontShareData("B");
ThreadDontShareData dontShareData3=new ThreadDontShareData("C");
dontShareData.start();
dontShareData2.start();
dontShareData3.start();
}
输出结果:
以上情况就是变量不共享,不存在多个线程共同访问一个实例变量的情况。
第二种共享数据
此种情况,顾名思义就是多个线程可以访问同一个变量,代码实例如下:
代码实例如下:
/**
*
* @author wodezuiaishinageren
* 共享变量
*/
public class ThreadShareData extends Thread {
private int count=5;
@Override
public void run(){
super.run();
count--;
//此处不使用for循环语句,因为使用同步后,其它线程必须等到for循环结束,一直由一个线程进行减法运算
System.out.println("当前线程:"+this.currentThread().getName()+"计算count="+count);
}
}
/**
*
* @author wodezuiaishinageren
* 线程共享数据测试
*
*/
public class ThreadShareDataTest {
public static void main(String[] args) {
ThreadShareData shareData=new ThreadShareData();
Thread a=new Thread(shareData,"A");
Thread b=new Thread(shareData,"B");
Thread c=new Thread(shareData,"C");
Thread d=new Thread(shareData,"D");
Thread e=new Thread(shareData,"E");
a.start();
b.start();
c.start();
d.start();
e.start();
}
}
输出结果(每次执行会不一样):
当前线程:B计算count=2
当前线程:C计算count=2
当前线程:A计算count=2
当前线程:D计算count=1
当前线程:E计算count=0
由以上输出结果可以看出,线程A,B,C打印出的值都是2,说明三个线程同时对count进行操作,产生了非线程安全问题,而大多数的实际情况中我希望是依次递减的。注:在某些jvm中,i—的操作分为3个小的步骤:1)取得原有的i值 2)进行i-1操作 3)对i进行赋值,在第3步中,如果有多个线程同时访问,那么就会出现非线程安全问题。
可以对以上代码进行改动,实现线程间的数据同步,而同步的操作有很多种,现列出其中的一种方式。
使用synchronized关键字
/**
*
* @author wodezuiaishinageren
* 用synchronized同步
*/
public class ThreadSyncShareData extends Thread {
private Integer count=5;
@Override
public synchronized void run(){
count--;
System.out.println("当前线程:"+this.currentThread().getName()+"计算count="+count);
}
}
输出结果:
当前线程:A计算count=4
当前线程:E计算count=3
当前线程:D计算count=2
当前线程:C计算count=1
当前线程:B计算count=0
通过在run方法前加入synchronized关键字,使每个线程在执行前先判断run()方法有没有被上锁,这样也就实现了排队调用run()方法的目的。synchronized可以在任意对象及方法上加锁,加锁的这段代码称为“互斥区”或者“临界区”。当一个线程想要执行一个同步方法时,先去判断此方法是否存在锁,如果没有则给方法加锁、执行,如果拿不到这把锁,则程序会不断的尝试获取这把锁,直到拿到为止,而且是多个线程同时去争抢。
-
3.2.3 isAlive()方法
此方法是判断当前线程是否处于活动状态。
实例代码如下:
/**
*
* @author wodezuiaishinageren
* 线程是否处于活动状态
*/
public class ThreadIsAliveMethod extends Thread{
@SuppressWarnings("static-access")
@Override
public void run(){
try {
System.out.println("线程名字:"+this.currentThread().getName()+"正将要结束:"+this.currentThread().isAlive());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("线程名字:"+this.currentThread().getName()+"结束:"+this.currentThread().isAlive());
}
}
/**
*
* @author wodezuiaishinageren
* 线程基本方法测试类
*/
public class ThreadMethodTest {
public static void main(String [] argd) throws InterruptedException{
ThreadIsAliveMethod aliveMethod=new ThreadIsAliveMethod();
isAlive(aliveMethod);
}
/**
* 线程是否处于活动状态
* @param thread
* @throws InterruptedException
*/
public static void isAlive(Thread thread) throws InterruptedException{
System.out.println("before:"+thread.isAlive());
thread.start();
Thread.sleep(1000L);
System.out.println("end:"+thread.isAlive());
}
输出结果,线程处于活动状态返回true,否则返回false,这个值不是准确的,所以在线程start后让main的线程停止1s等待ThreadIsAliveMethod执行完成后输出:
before:false
线程名字:Thread-0正将要结束:true
线程名字:Thread-0结束:true
end:false
-
3.2.4sleep()方法
sleep()是让当前正在执行线程在指定的时间内处于休眠状态,当前正在执行线程是指this.currentThread()返回的结果。
代码实例:
/**
*
* @author wodezuiaishinageren
* 线程sleep方法
*/
public class ThreadSleepMethod extends Thread{
@SuppressWarnings("static-access")
@Override
public void run(){
try {
System.out.println("当前线程:"+this.currentThread().getName()+"begin:"+System.currentTimeMillis());
Thread.sleep(1000);
System.out.println("当前线程:"+this.currentThread().getName()+"end:"+System.currentTimeMillis());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
*
* @author wodezuiaishinageren
* 线程基本方法测试类
*/
public class ThreadMethodTest {
public static void main(String [] argd) throws InterruptedException{
ThreadSleepMethod sleepMethod=new ThreadSleepMethod();
ThreadSleepMethod sleepMethod2=new ThreadSleepMethod();
sleepMethod(sleepMethod);
sleepMethod(sleepMethod2);
}
输出结果(可以得出sleep:不会释放同步锁,wait释放同步锁 )为:
当前线程:Thread-1begin:1461566103600
当前线程:Thread-2begin:1461566103600
当前线程:Thread-1end:1461566104602
当前线程:Thread-2end:1461566104602
sleep、wait、yield,都是发生在多多线程的情况下,在程序调用处,产生线程阻塞,join也会,但是它只等到被加入线程执行结束。
sleep 方法使当前运行中的线程睡眠一段时间,进入不可以运行状态,这段时间的长短是由程序设定的,yield方法使当前线程让出CPU占有权,但让出的时间是不可设定的。
yield()也不会释放锁标志。实际上,yield()方法对应了如下操作;先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给次线程,否则继续运行原来的线程,所以yield()方法称为“退让”,它把运行机会让给了同等级的其他线程。
sleep 方法允许较低优先级的线程获得运行机会,但yield()方法执行时,当前线程仍处在可运行状态,所以不可能让出较低优先级的线程此时获取CPU占有权。在一个运行系统中,如果较高优先级的线程没有调用sleep方法,也没有受到I/O阻塞,那么较低优先级线程只能等待所有较高优先级的线程运行结束,方可有机会运行。
yield()只是使当前线程重新回到可执行状态,所有执行yield()的线程有可能在进入到可执行状态后马上又被执行,所以yield()方法只能使同优先级的线程有执行的机会。