线程的创建方法
java中创建线程的方法可以说只有一个,就是通过new Thread()的方式来创建线程。但是,从使用的角度来看,java中创建线程的方式可以说是有多种的:
1 第一种可以通过继承 Thread 类,重写 Thread 类中的 run() 函数来实现线程逻辑。然后在主类中通过新建一个Thread的实现类来开启线程。
2 实现Runnable接口,重写 run() 方法即可。采用接口的方式来创建线程相对而言更加灵活,毕竟java只支持单继承,继承Thread类就没办法继承自己的类啦。
需要注意的是,线程的启动只能调用 .start()方法来运行。如果直接调用run()函数,则相当于在主线程中调用run()函数,最终的效果是串行的,而不是多线程的方式。调用start方法相对于让创建的线程进入就绪态,具体什么时候由cpu执行是程序无法控制的。程序本身是不能决定线程什么时间被CPU执行的。
继承 Thread 类
继承Thread类的创建方法,
public class MyThread extends Thread{
public void run() {
while(true) {
System.out.println("mythread is running");
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
直接创建调用start方法即可
MyThread tesThread = new MyThread();
tesThread.start();
实现Runnable
创建实现Runnable的类,重写run()方法即可。
public class MyThreadRunnable implements Runnable{
private String name;
public MyThreadRunnable(String name) {
this.name = name;
// TODO Auto-generated constructor stub
}
public void setName(String name) {
this.name = name;
}
public void run() {
while(true) {
System.out.println(name + " runnable is running");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
调用方法,不过对于同一个Runnable来说,其start方法只能被调用一次。若想多次调用,需要新建Runnale接口方法
MyThreadRunnable test1 = new MyThreadRunnable("test");
Thread testRunnable = new Thread(test1);
testRunnable.start();
线程简单的共享变量
继承Thread的类每一个类只能调用一次start函数,多次new一个继承类属于操作不同的变量,这种方法是无法共享变量的。如每个Thread类都有变量 x,那么执行两次 new Thread() 中的变量是不可互相看到的。
若想让每个类都可以看到其中的一些变量,一种可行方法是需要将变量设置为static变量。所有相同类型的类共享一份。
Runnable接口类则不同,实现Runnable接口的类只会保存一份变量,所以线程对类中变量的操作都是相同的。不过可以可以共享变量并不意味着线程安全,变量的改变并不是原子性的,在一个线程操作的同时,其值可能已经被其它线程修改。另外,线程对变量的操作是操作自己工作内存中的一个副本,和内存中的变量值可能不一致。很常见的一个例子如下,在主线程中调用flag=false并不会终止线程,因为线程一直在用自己缓存中的数据,而没有从主存中更新变量的值。
public class MyThreadRunnable implements Runnable{
public boolean flag = true;
public void run() {
while(flag) {
System.out.println(name + " runnable is running");
}
}
因此,在共享变量的同时,还需要考虑变量的对每个线程的可见性。简单的方法是volatile关键字。
volatile 关键字
volatile 让每个线程中对变量的拥有立即的可见性。只要volatile修饰的变量修改后,所有线程中都可以看到这种改变。在底层,这个可见性主要是通过volatile关键字修饰的变量必须修改后立即写入内存,使用前读入线程的缓存。在上面的程序中,把flag用volatile修饰,则当flag改变时,线程可立即看到这种变化。
public class MyThreadRunnable implements Runnable{
public volatile boolean flag = true;
public void run() {
while(flag) {
System.out.println(name + " runnable is running");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}