线程的创建方法

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();
			}
		}
	}
}