join 线程
Thread提供的一个线程等待另一个线程执行完成的方法。当某个线程调用其他线程的join()方法是,当前这个线程会中断,知道被join()线程执行完毕后才开始执行。
join()方法的重载
- join():等待join的线程执行完毕。
join(long millis):如果等待时间超过了参数的时间,就不再等待。
public class Join extends Thread{ public static void main(String[] args) { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+i); if(i==10){ try { //被join()的线程 Thread t = new Join(); t.start(); //调用join()后会执行t线程,t执行完毕之后再执行主线程 t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } public void run(){ for(int i=0;i<100;i++){ System.out.println(getName()+i); } } }
后台进程
在后台执行,为其它线程提供服务。比如JVM的垃圾收集器就是典型的后台进程。
后台进程的的特点:如果前台的线程都死亡了,后台进程会自动死亡。
调用thread对象的setDaemon(true)方法可以将线程设置成后台进程,此方法只能在start()方法之前调用。
public class TestDaemon extends Thread {
public static void main(String[] args) {
TestDaemon t = new TestDaemon();
//将t线程设置成后台进程,并且启动。当主线程执行完是t线程自动停止。
t.setDaemon(true);
t.start();
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);
}
}
public void run(){
for(int i=0;i<1000;i++){
System.out.println(getName()+i);
}
}
}
sleep 和yield 方法
sleep是Thread的静态方法,调用后正在执行的线程会进入阻塞状态,等待一段时间后执行。即使只有一个线程也会暂停。
yield()方法也是Thread的静态方法,他可以让当前的线程暂停但是不会阻塞该线程。线程会转入就绪状态,如果cup继续调用这个线程,那么这个线程就还会执行。
public class SleepTesst {
public static void main(String[] args) {
//每间隔一秒打印一个数字
for(int i=0;i<10;i++){
try {
Thread.sleep(1000);
System.out.println(i) ;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class TestYield extends Thread{
TestYield(String name){
super(name) ;
}
public static void main(String[] args) {
TestYield t = new TestYield("thread1_");
TestYield t2 = new TestYield("thread2_");
t.start() ;
t2.start() ;
}
public void run(){
for(int i=0;i<50;i++){
if(i==30){
Thread.yield();
}
System.out.println(getName()+i) ;
}
}
}
线程同步
多个线程操作同一个资源的时候,会出现线程安全的问题。
使用同步代码块。synchronized(obj)obj就是同步监视器,在执行代码块里面的代码时先要获得同步监视器的锁定。
任何时刻只有一个线程可以获得同步监视器的锁定,当同步代码块执行完成后,该线程会释放同步监视器的锁定
public class Account implements Runnable {
int num = 20 ;
Object obj = new Object();
public static void main(String[] args) {
Account account = new Account();
Thread t = new Thread(account,"thread1") ;
Thread t2 = new Thread(account,"thread2") ;
t.start();
t2.start();
}
@Override
public void run() {
synchronized(obj){
if(num>0){
for(int i=0;i<10;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">>>"+num--);
}
}
}
}
}
使用同步方法:
使用synchronized修饰的方法。对于实例的同步方法,无需显示的指定同步监视器。同步监视器是this,也就是调用该方法的对象。
静态的同步函数使用的锁是该函数所属字节码文件对象,可以使用getClass方法获取,也可以使用类名.class表示。
当同步方法和同步代码块的同步监视器是一样的时候,可以实现同步。
public class Account implements Runnable {
int num = 20 ;
public Boolean flag = true ;
public static void main(String[] args) {
Account account = new Account();
Thread t = new Thread(account,"thread1") ;
Thread t2 = new Thread(account,"thread2") ;
//实际执行是同步代码块中的代码
t.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//将flag设置成flase后调用就是show()方法。
account.flag=false ;
t2.start();
}
//同步方法,同步监视器为调用该方法的对象。与下面的同步代码块的监视器相同
//那么同步方法与同步代码块只能有一个被线程调用
public synchronized void show(){
if(num>0){
for(int i=0;i<10;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">>>"+num--);
}
}
}
@Override
public synchronized void run() {
if(flag){
if(num>0){
//同步代码块,同步监视器为调用的对象
synchronized(this){
for(int i=0;i<10;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+">>>"+num--);
}
}
}
}else{
show();
}
}
}
死锁
当两个线程相互等待对方释放同步监视器时就会发生死锁。一旦出现死锁,程序既不会发生任何异常,也不会出现任何提示。
public class DeathThread implements Runnable {
A a = new A() ;
B b = new B() ;
public static void main(String[] args) {
DeathThread run = new DeathThread() ;
Thread t = new Thread(run,"thread") ;
t.start();
run.init();
}
public void init(){
b.show(a);
}
public void run(){
a.show(b);
}
}
class A {
//执行完show()方法要拿到两把锁,a和b
public synchronized void show(B b){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
b.last();
}
public synchronized void last(){
System.out.println(Thread.currentThread().getName()+"int");
}
}
class B{
//执行完show()方法要拿到两把锁,b和a
public synchronized void show(A a){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
a.last();
}
public synchronized void last(){
System.out.println(Thread.currentThread().getName()+"in");
}
}
刚开始 子线程和主线程分别来到了a和b的锁。睡眠1秒后向下执行,子线程有a的锁还需要b的锁,而主线程有b的锁还需要a的锁。这样就发生了死锁。
另一种死锁的情况:
public class DeathThread2 {
public static void main(String[] args) {
MyRun run = new MyRun() ;
Thread t1 = new Thread(run) ;
Thread t2 = new Thread(run) ;
//t1拿到obj的锁,往下执行需要this的锁
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
run.setFlag();
//t2先拿到this的锁,往下执行需要obj的锁
t2.start();
}
}
class MyRun implements Runnable{
Object obj = new Object();
Boolean flag = true ;
//run()方法需要两把锁 obj和this。
@Override
public void run() {
if(flag){
synchronized(obj){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
show() ;
}
}else{
show() ;
}
}
//show方法需要两把锁,this和obj。
synchronized void show(){
System.out.println(Thread.currentThread().getName()) ;
synchronized(obj){
System.out.println(Thread.currentThread().getName()+"show+in") ;
}
}
void setFlag(){
flag = false ;
}
}
`死锁的发生是由于执行完一个线程需要两个或是多个锁,在自己拿到一个锁后还要其他锁(其他锁已经被别的线程锁定且