1.线程的生命周期
2.新建线程
实现Runnable接口中的run()方法
1 public class User implements Runnable { 2 3 @Override 4 public void run() { 5 System.out.println("run"); 6 } 7 8 public static void main(String[] args) { 9 Thread t = new Thread(new User()); 10 t.start(); 11 } 12 }
3.中断线程
- 为线程设置标志位,在run()方法中设定结束条件,在其他线程修改了标志位以后,退出线程。
不要使用stop()方法结束线程,使用stop会使线程释放它持有的锁,导致数据的不一致性。如:用线程读写user,这个user本来id=1,name=1,写线程使用stop方法结束,导致刚写完user的id=1线程的锁就被释放了,此时有一个线程读取了这个user,有可能会读出user的id=1,name=0这种不一致结果。
1 package com.company; 2 public class User implements Runnable { 3 public volatile boolean exit = false; 4 @Override 5 public void run() { 6 while (true){ 7 if (exit) { 8 System.out.println("exit"); 9 break; 10 } 11 System.out.println("running"); 12 } 13 } 14 15 public static void main(String[] args) throws InterruptedException { 16 User u = new User(); 17 Thread t = new Thread(u); 18 t.start(); 19 Thread.sleep(10); 20 u.exit = true; 21 } 22 }
- 使用interrupt()方法中断线程,不是立即中断,而是告诉目标线程,有人希望你退出了。
interrupt()相当于设置了中断标志,需要在运行的线程当中捕获这个标志并进行处理。
下面的程序每隔0.5秒输出一个数字,5秒后设置中断,运行的线程处理中断的逻辑是退出线程,因此输出到9程序退出。
注意sleep()方法在休眠过程中,被中断会抛出中断异常,抛出中断异常后会清除中断标志,如果不在sleep()的中断块中处理中断逻辑,需要重新设置中断标志。
1 package com.company; 2 3 public class User implements Runnable { 4 private volatile int i = 0; 5 @Override 6 public void run() { 7 while(true){ 8 if(Thread.currentThread().isInterrupted()){ 9 System.out.println("exit"); 10 break; 11 } 12 try { 13 Thread.sleep(500); 14 } catch (InterruptedException e) { 15 //e.printStackTrace(); 16 Thread.currentThread().interrupt(); 17 } 18 System.out.println(i++); 19 } 20 } 21 22 public static void main(String[] args) throws InterruptedException { 23 Thread t = new Thread(new User()); 24 t.start(); 25 Thread.sleep(5000); 26 t.interrupt(); 27 } 28 }
4.wait()和notify()
wait()和notify()方法是Object的方法,这2个方法用来完成线程之间的协作。
wait()和notify()方法需要持有对象的锁,因此必须放在synchronized块中。
某个线程t1内调用一个对象object.wait()方法,会将t1线程加入到object的等待队列中,等待被notify(),注意notify()是随机唤醒,不是顺序唤醒。
1 package com.company; 2 3 import java.text.SimpleDateFormat; 4 5 public class Main { 6 private static Main obj = new Main(); 7 public static class WaitThread extends Thread{ 8 9 @Override 10 public void run() { 11 synchronized (obj){ 12 System.out.println("等线程启动!"); 13 try { 14 obj.wait(); 15 } catch (InterruptedException e) { 16 } 17 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 18 System.out.println(df.format(System.currentTimeMillis())); 19 System.out.println("等线程结束!"); 20 } 21 } 22 } 23 public static class NotifyThread extends Thread{ 24 @Override 25 public void run() { 26 synchronized (obj){ 27 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 28 System.out.println(df.format(System.currentTimeMillis())); 29 System.out.println("唤醒线程启动!"); 30 obj.notify(); 31 System.out.println("唤醒!"); 32 try { 33 Thread.sleep(2000); 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } 37 System.out.println("释放锁!"); 38 } 39 } 40 } 41 public static void main(String[] args) { 42 Thread t1 = new WaitThread(); 43 Thread t2 = new NotifyThread(); 44 t1.start(); 45 t2.start(); 46 47 } 48 }
5.join
join使当前线程等待某个线程执行完了以后在执行下面的代码。
package com.company; public class User implements Runnable { public static volatile int i = 0; @Override public void run() { for(;i<10000;i++); } public static void main(String[] args) throws InterruptedException { Thread t = new Thread(new User()); t.start(); t.join(); System.out.println(i); } }
注释了join的话,有可能会输出0,表示t线程还没有执行完,主线程就输出了。
6.线程组
把线程加到不同的组里,方便管理。
1 package com.company; 2 3 public class User implements Runnable { 4 @Override 5 public void run() { 6 System.out.println(Thread.currentThread().getThreadGroup().getName() + Thread.currentThread().getName()); 7 } 8 9 public static void main(String[] args) throws InterruptedException { 10 ThreadGroup gp = new ThreadGroup("printgp"); 11 Thread t1 = new Thread(gp,new User(),"t1"); 12 Thread t2 = new Thread(gp,new User(),"t2"); 13 t1.start(); 14 t2.start(); 15 } 16 }
7.守护线程
守护线程是为用户线程默默服务的线程,如果系统里只有守护线程,那么程序会退出。
守护线程不停的打印i的值,但是用户线程main在3秒以后退出,所以main线程结束后,守护线程也结束了。
1 package com.company; 2 3 public class User implements Runnable { 4 @Override 5 public void run() { 6 int i = 0; 7 while(true){ 8 System.out.println(i++); 9 try { 10 Thread.sleep(1000); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 } 16 17 public static void main(String[] args) throws InterruptedException { 18 Thread t = new Thread(new User()); 19 t.setDaemon(true); 20 t.start(); 21 Thread.sleep(3000); 22 } 23 }
8.线程优先级
线程优先级从1-10,在抢占资源时,高优先级的线程会比低优先级的线程更容易抢占到资源。
经过几秒钟的实验,优先级高的能比优先级低的抢占更多次资源。
1 package com.company; 2 3 public class User implements Runnable { 4 public static volatile int lowNum = 0; 5 public static volatile int maxNum = 0; 6 public static volatile int runNum = 0; 7 @Override 8 public void run() { 9 while(true){ 10 synchronized (User.class){ 11 runNum++; 12 if(Thread.currentThread().getName() == "low") 13 lowNum++; 14 else maxNum++; 15 } 16 System.out.println("总比赛次数"+runNum+",low获胜次数"+lowNum+",max获胜次数"+maxNum); 17 } 18 } 19 20 public static void main(String[] args) throws InterruptedException { 21 Thread t1 = new Thread(new User(),"low"); 22 Thread t2 = new Thread(new User(),"max"); 23 t1.setPriority(Thread.MIN_PRIORITY); 24 t2.setPriority(Thread.MAX_PRIORITY); 25 t1.start(); 26 t2.start(); 27 } 28 }
9.synchronized
synchronized关键字把某段代码设定为blocked状态,blocked状态的代码块,一次只能有一个线程进入,从而使某些公共资源对所有的线程来讲都是同步的。
未进行同步的代码,最终输出的i值都到不了100000000.
package com.company; public class User implements Runnable { private static User user = new User(); private static int i = 0; @Override public void run() { for (int j = 0; j < 100000000; j++) { i++; } System.out.println(i); } public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new User()); Thread t2 = new Thread(new User()); t1.start(); t2.start(); } }
三种方法同步
- 同步实例对象
1 package com.company; 2 3 public class User implements Runnable { 4 static User user = new User(); 5 static int i = 0; 6 @Override 7 public void run() { 8 //对实例对象user同步 9 synchronized (user){ 10 for (int j = 0; j < 10000000; j++) { 11 i++; 12 } 13 System.out.println(i); 14 } 15 } 16 17 public static void main(String[] args) throws InterruptedException { 18 //保证2个线程使用同一个实例对象创建 19 Thread t1 = new Thread(user); 20 Thread t2 = new Thread(user); 21 t1.start(); 22 t2.start(); 23 } 24 }
- 同步实例方法
1 package com.company; 2 3 public class User implements Runnable { 4 static User user = new User(); 5 static int i = 0; 6 //使用synchronized同步实例方法 7 public synchronized void increase(){ 8 for (int j = 0; j < 10000000; j++) { 9 i++; 10 } 11 System.out.println(i); 12 } 13 @Override 14 public void run() { 15 increase(); 16 } 17 18 public static void main(String[] args) throws InterruptedException { 19 //保证2个线程使用同一个实例对象创建 20 Thread t1 = new Thread(user); 21 Thread t2 = new Thread(user); 22 t1.start(); 23 t2.start(); 24 } 25 }
- 同步类
1 package com.company; 2 3 public class User implements Runnable { 4 static int i = 0; 5 @Override 6 public void run() { 7 //同步类 8 synchronized (User.class) { 9 for (int j = 0; j < 10000000; j++) { 10 i++; 11 } 12 System.out.println(i); 13 } 14 } 15 16 public static void main(String[] args) throws InterruptedException { 17 //保证2个线程使用同一个实例对象创建 18 Thread t1 = new Thread(new User()); 19 Thread t2 = new Thread(new User()); 20 t1.start(); 21 t2.start(); 22 } 23 }