Java多线程学习笔记
1、继承Thread类
实现多线程的常用方法就是继承Thread类,然后覆盖run()方法。下面是一个通过继承Thread类实现多线程的小例子:
import java.util.Date;
public class ThreadTest extends Thread{
@Override
public void run() { // 重写run方法
System.out.println(this.getName() + " " + new Date().toString());
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadTest t1 = new ThreadTest();
t1.start();
ThreadTest t2 = new ThreadTest();
t2.start();
}
}
运行结果如下:
Thread类实际上是Runnable接口的实现,
public class Thread implements Runnable {
...
}
Thread类中,有一些方法比较常用:
线程在创建后,需要调用start()才能运行,然后线程会自动调用run()方法,所以在子类中需要覆盖run()方法。注意,我们要用start()来启动线程,run()方法仅仅是一个普通的方法。
sleep()和suspend()都可以让线程暂停执行,但是二者的区别如下:
(1).sleep()使线程休眠后,只能在时间结束后,线程才能恢复到就绪状态;而suspend()挂起线程后,可以通过resume()唤醒线程。
(2).另外,sleep()仅对当前线程起作用,而suspend()可以挂起另一个线程。yield()是一个静态的native方法,它的作用是“告诉当前正在执行的线程,把运行机会交给线程池中拥有相同优先级的线程”,说白了就是把机会留给别人。
stop()和interrupt()都可以中断线程,但二者区别在于stop强制终止线程,而interrupt()中断线程。关于二者的具体介绍,详见:http://blog.csdn.net/wxwzy738/article/details/8516253。这里插一句,如果我们要中断线程,最好设置一个共享变量,然后在run方法中,周期性地检查该变量的值,满足终止条件时再终止线程。
为了加深理解,现附上线程生命周期图(图片来自百度百科):
2、实现Runnable接口
Runnable接口的源码如下,只有一个抽象方法run(),因此实现这个接口的核心就是覆盖run()方法。
public interface Runnable {
public abstract void run();
}
下面是一个小例子,这个例子实现了Runnable接口
import java.util.Date;
public class ThreadTest implements Runnable{
String name;
ThreadTest(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(this.name + " " + new Date().toString());
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadTest t1 = new ThreadTest("Thread1");
new Thread(t1).start();
ThreadTest t2 = new ThreadTest("Thread2");
new Thread(t2).start();
}
}
运行结果如下:
3、join方法
无join()时,主线程调用start()后,程序立即返回到主线程,此时主线程和子线程各运行各的,也就是我们常说的“异步”。
有join()时,主线程调用start()后,会一直等待子线程执行完毕,再往下执行,这也就是“同步”。
下面看两个小例子:
import java.util.Date;
public class ThreadTest extends Thread{
@Override
public void run() {
while(true){
try {
this.sleep(1000);
System.out.println(this.getName() + " " + new Date().toString());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadTest t1 = new ThreadTest();
t1.start();
ThreadTest t2 = new ThreadTest();
t2.start();
// 没有join() 直接返回到主线程
System.out.println("t1,t2 stop");
}
}
运行结果显示,主线程在启动t1、t2两个子线程后,直接返回到主线程继续执行 System.out.println(“t1,t2
stop”);
下面我们加上join()方法,同时把while去掉,避免程序进入死循环无法返回。
import java.util.Date;
public class ThreadTest extends Thread{
@Override
public void run() {
// 去掉了while(true)的循环
try {
this.sleep(1000);
System.out.println(this.getName() + " " + new Date().toString());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException { // 注意要抛出异常
// TODO Auto-generated method stub
ThreadTest t1 = new ThreadTest();
t1.start();
ThreadTest t2 = new ThreadTest();
t2.start();
t1.join(); // t1添加join()
t2.join(); // t2添加join()
System.out.println("t1,t2 stop");
}
}
来看看效果吧,可以看到,程序直到t1、t2线程结束后,才返回执行System.out.println(“t1,t2 stop”);
4、synchronized关键字
synchronized知识点总结:
1、synchronized可以修饰代码块、对象和方法,不能用于修饰int、char、boolean等简单类型的变量。
synchronized int a = 0; // 错误,synchronized 不能修饰int类型变量
2、synchronized不能被继承。若父类中有一个方法使用了synchronized修饰,子类中覆盖了这个方法,子类中的这个方法默认不是同步的,必须加synchronized关键词显示地生命同步。
3、synchronized不能放在返回类型后面,比如下面这句代码就是错误的。
void synchronized func() // 错的,synchronized不能放在返回值后面
synchronized关键字可以修饰代码块:
class Test {
private final static String key = ""; // 注意要设置为final
synchronized(key) {
// TODO 这里是同步的代码块
// 注意,这里不能改变key的值,否则不能同步
// 因此要把key设置为final
}
}
synchronized关键字也可以用来修饰方法,请看下面的例子:在单件模式中使用synchronized关键字。
5、用synchronized关键字实现单件模式
class Singleton {
private static Singleton instance;
private Singleton() {
}
// 使用synchronized 关键字修饰方法
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
当然,单件模式还有更简单的实现方式:
public class Singleton {
private final static Singleton instance = new Singleton(); // 使用“急切”
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
6、实例:使用多线程打印数字
public class ThreadTest implements Runnable {
private int num = 0;
@Override
public synchronized void run() {
for(int i = 0; i < 10; i++) {
System.out.println(this.num);
num++;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadTest t1 = new ThreadTest();
new Thread(t1).start(); // 打印0-9
new Thread(t1).start(); // 打印10-19
new Thread(t1).start(); // 打印20-29
}
}
程序的运行结果如下:
今天就到这里,拜~