Thread和Runnable类

来源:互联网 发布:depthmap软件原理 编辑:程序博客网 时间:2024/06/06 05:57
 
    Thread和Runnable类


    
通常看到网上都会说两种实现线程都可以,但是自己还是有些地方不明白的就是说两个的区别
Thread有利于资源共享,Runnable不利于资源共享,但是我自己写的没有按照常规出牌,所以没有整明白。
我实现一个类似多线程卖票的细节,由于是双核4线程cpu,不是单核的,所以就需要考虑同步问题来解决资源共享问题。


由于一开始我票的数量是用static的变量来表示的,所以尽管加了同步,还是没有效果,最后都出现了重复的数字,就是类似一张票被两个人都买到了的情况,
这里由于线程实例是一个对象,而线程是的同步也是针对对象的,我的static变量是与对象没有关系的。


网上的也是大多数都是用成员变量来演示的。
看下面


package com.te;


import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


class Y extends Thread {
int count = 1000;// 火车票
static long currenttime;
static long endtime;
static File file = new File("C:/Users/绝影/Desktop/log.txt");
static OutputStream out = null;
static OutputStreamWriter output = null;
static {
System.out.println("我是静态块");
try {
out = new FileOutputStream(file);
output = new OutputStreamWriter(out);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}


public void run() {


try {
try {
sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
one();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}


public void one() throws IOException {
// TODO 线程输出方法
try {
if (count > 0) {
synchronized (this) {
output.write("车票" + (count--) + "正在      " + Thread.currentThread().getName() + "   出售" + "\r\n");
System.out
.println("车票" + (count) + "正在      " + Thread.currentThread().getName() + "   出售" + "\r\n");
}
} else {
endtime = System.currentTimeMillis();
System.out.println(endtime);
System.out.println("抱歉现在没有车票了" + "总共用了时间" + (endtime - currenttime) + "卖完完余票");
System.exit(0);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
output.flush();
}


}
}


public class C implements Runnable {
int count = 1000;// 火车票
static long currenttime;
static long endtime;
static File file = new File("C:/Users/绝影/Desktop/log.txt");
static OutputStream out = null;
static OutputStreamWriter output = null;
static {
System.out.println("我是静态块");
try {
out = new FileOutputStream(file);
output = new OutputStreamWriter(out);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


}


public void one() throws IOException {


// TODO 线程输出方法
try {
if (count > 0) {
synchronized (this) {
output.write("车票" + (count--) + "正在      " + Thread.currentThread().getName() + "   出售" + "\r\n");
System.out
.println("车票" + (count) + "正在      " + Thread.currentThread().getName() + "   出售" + "\r\n");
}
} else {
endtime = System.currentTimeMillis();
System.out.println(endtime);
System.out.println("抱歉现在没有车票了" + "总共用了时间" + (endtime - currenttime) + "卖完完余票");
System.exit(0);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
output.flush();
}


}


@Override
public void run() {
// TODO Auto-generated method stub
try {
one();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}


public static void main(String[] args) throws InterruptedException {
currenttime = System.currentTimeMillis();
final C output = new C();
final Y y = new Y();
System.out.println("当前时间" + currenttime);


for (int j = 1; j <= 1000; j++) {


// 继承Thread
//new Y().start();// 不利于资源共享,由于每次new出的对象都是新的,所以操作的都是自己对象的成员变量,所以执行完1000个线程的结果是只卖出一张票


// 实现Runnable接口的
new Thread(output).start();// 可以共享资源,因为都是同一个对象的成员变量,共享变量加锁后不会出现问题,执行完后卖出1000张


}


}


}






但是事实上我自己最开始编写的不是这个样子
1000个线程的循环体中我试过这些,下面分别说说


y.start();//会报错Exception in thread "main" java.lang.IllegalThreadStateException,
下面源码中可以看出之所以多线程要调用start方法而不是直接调用run方法,
源码中调用了方法native的方法start0(),是因为需要调用调用该操作系统调用底层函数来开启一个线程,如果直接调用run方法就直接和调用普通方法一样就没有任何意义。
还有一个变量 started可以看出用来判断线程开启成功与否,另外threadStatus变量判断不等于0就抛出上面这个异常,A zero status value corresponds to state "NEW".
这个的大概意思就是这个变量表示线程状态是不是新的,所以如果我们反复用一个对象调用start那么除了第一次外后面自然都是非new的,所以就抛出这个异常


   public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();


        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);


        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }


    private native void start0();//


为了更好的分析我写的几种特殊情况先分析线程对象和线程的区别
1.继承Thread类:new Y().start()
线程对象就是线程类的实例对象,它是通过继承thread类或者通过实现runnable接口得到。
所以继承Thread的时线程对象就是其子类。所以也就相当于给每个新的线程对象开一个线程,自然不适合资源共享。如果一个任务的各个线程之间没有资源共享那么。
这个时候就适合用这种多线程。


2.实现Runnable接口:new Thread(output).start();
始终是给一个线程对象开了多个线程,适合一个线程对象完成一个任务,而一个任务可以分开给几个线程共享资源共同完成一件事,这时候就适合用这种,所以由于接口的优势还有资源的共享
实现接口方式的多线程应用比较广。


解释下面我试过的几种情况
继承Thread类循环体中我写了如下:
  new Thread(y).start();
//这里我纠集了好久,网上说Thread来实现线程不利于资源共享,但是我这样写没有任何问题,达到了我想要的结果,可以实现共享,我理解的意思应该可能是说排除这种情况
因为我new Thread()来调用后感觉就失去了继承Thread的意义,就好像是实现Runnable接口的情况,这样写后只是相当于调用了继承了Thread类中的run方法,
真正的标准写法应该是new Y().start();
所以不能说不能实现共享,应该是用的多的一句话不容易实现共享。


下面这种是实现接口的情况,这种就可以达到资源共享的目的。整个过程中只有一个线程对象的存在
 new Thread(output).start();
但是我们也可以写出多个线程对象的情况,这里也是类似上面那种,这样也不能实现资源共享,
所以我想的是应该这两种写法不是标准的实现方法才会有不利于资源共享的说法。
 new Thread(new C()).start();


引用:

不同的线程在synchronized块里,同时只有一个线程能执行该代码块,而类的不同实例对象之间是互不影响的。比如Person A = new Person(),Person B = new Person().A和B之间是不受synchronized制约的。


同时也解释了为什么我变量事static形式时加了synchronized关键字无效,我想是因为synchronized是和线程对象相关的,我的this指的是线程对象,我的线程对象每次都变,我加了自然也就没效果
synchronized只能对同一个对象的多个线程同步,而不能对不同的线程对象同步,和static的String对象道理是一样的都无效果,因为我的String类被定义成为了final型的,也就是不可变的对象,我每次对它的修改都会创建新的对象,所以自然也就没有同步效果了,我把static int count换成了static Integer count后还是一样会出现重复数据,synchronized没有作用,结果一查发现Integer 果然是final类型的
加了同步块没效果,那就还会有线程对共享资源访问的安全问题。


为了验证我自己的猜想所以定义了一个非final的对象。
private static Queue<String> queue = new LinkedBlockingQueue();//在同步块中定义一个队列来存储每次修改的count数据    
用这个对象来实现同步,问题解决,数据不在重复。因为每个线程调用同步块都会修改对象queue ,同一时刻只能是一个线程进入同步块,但是queue对象还是同一个,修改没有产生新对象,直接放类名点class,如String.class也是没有问题的。只要不产生新对象。
可以理解这里是为了queue 安全修改而实现的同步锁,我们把需要修改的数据放入了队列里面,只是一时找不到合适的替代对象来实现,就用这个来加锁。
synchronized(queue) {
output.write("车票"+(count--)+"正在      "+Thread.currentThread().getName()+"   出售"+"\r\n");
System.out.println("车票"+(count)+"正在      "+Thread.currentThread().getName()+"   出售"+"\r\n");

  }


下面这个如果我其它线程中存在有等待线程对象的情况并且我调用了唤醒方法
肯定会抛出异常,出现唤醒的线程对象不存在,因为我每次都new了当前对象,而没有wait的情况。同步没效果我开了多线程我是多核CPU自然出现数据重复的情况。


synchronized(this) {
output.write("车票"+(count--)+"正在      "+Thread.currentThread().getName()+"   出售"+"\r\n");
System.out.println("车票"+(count)+"正在      "+Thread.currentThread().getName()+"   出售"+"\r\n");
  }
原创粉丝点击