线程的创建和启动
java语言已经内置了多线程支持所有实现Runnable接口的类都可被启动一个新线程新线程会执行该实例的run()方法当run()方法执行完毕后线程就结束了一旦一个线程执行完毕这个实例就不能再重新启动只能重新生成一个新实例再启动一个新线程
Thread类是实现了Runnable接口的一个实例它代表一个线程的实例并且启动线程的唯一方法就是通过Thread类的start()实例方法
Thread t = new Thread();
tstart();
start()方法是一个native方法它将启动一个新线程并执行run()方法Thread类默认的run()方法什么也不做就退出了注意直接调用run()方法并不会启动一个新线程它和调用一个普通的java方法没有什么区别
因此有两个方法可以实现自己的线程
方法自己的类extend Thread并复写run()方法就可以启动新线程并执行自己定义的run()方法例如
public class MyThread extends Thread {
public run() {
Systemoutprintln(MyThreadrun());
}
}
在合适的地方启动线程new MyThread()start();
方法如果自己的类已经extends另一个类就无法直接extends Thread此时必须实现一个Runnable接口
public class MyThread extends OtherClass implements Runnable {
public run() {
Systemoutprintln(MyThreadrun());
}
}
为了启动MyThread需要首先实例化一个Thread并传入自己的MyThread实例
MyThread myt = new MyThread();
Thread t = new Thread(myt);
tstart();
事实上当传入一个Runnable target参数给Thread后Thread的run()方法就会调用targetrun()参考JDK源代码
public void run() {
if (target != null) {
targetrun();
}
}
线程还有一些Name ThreadGroup isDaemon等设置由于和线程设计模式关联很少这里就不多说了
线程同步
由于同一进程内的多个线程共享内存空间在Java中就是共享实例当多个线程试图同时修改某个实例的内容时就会造成沖突因此线程必须实现共享互斥使多线程同步
最简单的同步是将一个方法标记为synchronized对同一个实例来说任一时刻只能有一个synchronized方法在执行当一个方法正在执行某个synchronized方法时其他线程如果想要执行这个实例的任意一个synchronized方法都必须等待当前执行synchronized方法的线程退出此方法后才能依次执行
但是非synchronized方法不受影响不管当前有没有执行synchronized方法非synchronized方法都可以被多个线程同时执行
此外必须注意只有同一实例的synchronized方法同一时间只能被一个线程执行不同实例的synchronized方法是可以并发的例如class A定义了synchronized方法sync()则不同实例async()和async()可以同时由两个线程来执行
Java锁机制
多线程同步的实现最终依赖锁机制我们可以想象某一共享资源是一间屋子每个人都是一个线程当A希望进入房间时他必须获得门锁一旦A获得门锁他进去后就立刻将门锁上于是BCD就不得不在门外等待直到A释放锁出来后BCD中的某一人抢到了该锁(具体抢法依赖于JVM的实现可以先到先得也可以随机挑选)然后进屋又将门锁上这样任一时刻最多有一人在屋内(使用共享资源)
Java语言规范内置了对多线程的支持对于Java程序来说每一个对象实例都有一把锁一旦某个线程获得了该锁别的线程如果希望获得该锁只能等待这个线程释放锁之后获得锁的方法只有一个就是synchronized关键字例如
public class SharedResource {
private int count = ;
public int getCount() { return count; }
public synchronized void setCount(int count) { unt = count; }
}
同步方法public synchronized void setCount(int count) { unt = count; } 事实上相当于
public void setCount(int count) {
synchronized(this) { // 在此获得this锁
unt = count;
} // 在此释放this锁
}
红色部分表示需要同步的代码段该区域为危险区域如果两个以上的线程同时执行会引发沖突因此要更改SharedResource的内部状态必须先获得SharedResource实例的锁
退出synchronized块时线程拥有的锁自动释放于是别的线程又可以获取该锁了
为了提高性能不一定要锁定this例如SharedResource有两个独立变化的变量
public class SharedResouce {
private int a = ;
private int b = ;
public synchronized void setA(int a) { thisa = a; }
public synchronized void setB(int b) { thisb = b; }
}
若同步整个方法则setA()的时候无法setB()setB()时无法setA()为了提高性能可以使用不同对象的锁
public class SharedResouce {
private int a = ;
private int b = ;
private Object sync_a = new Object();
private Object sync_b = new Object();
public void setA(int a) {
synchronized(sync_a) {
thisa = a;
}
}
public synchronized void setB(int b) {
synchronized(sync_b) {
thisb = b;
}
}
}