public class ThreadDemo extends Thread{ @Override public void run(){ //编写自己的线程代码 System.out.println(Thread.currentThread().getName()); } public static void main(String[] args){ ThreadDemo threadDemo = new ThreadDemo(); threadDemo.setName("我是自定义的线程1"); threadDemo.start(); System.out.println(Thread.currentThread().toString()); } }
优点 :代码简单 。 缺点 :该类无法集成别的类。
通过实现Runnable接口,实现run方法,接口的实现类的实例作为Thread的target作为参数传入带参的Thread构造函数,通过调用start()方法启动线程
public class MyThreadRunable implements Runnable{ public static void main(String[] arg){ MyThreadRunable runable = new MyThreadRunable(); Thread myThread = new Thread(runable); myThread.start(); System.out.println("step2"); } @Override public void run() { System.out.println("step1"); try { sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }
优点 :继承其他类。 同一实现该接口的实例可以共享资源。
缺点 :代码复杂
1)创建Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,且该 call() 方法有返回值 。
2)创建Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象, 该 FutrueTask 对象封装了该 Callable 对象的 call() 方法的返回值。
3)使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
public class ThreadDemo { public static void main(String[] args) { Callable<Object> oneCallable = new Tickets<Object>(); FutureTask<Object> oneTask = new FutureTask<Object>(oneCallable); Thread t = new Thread(oneTask); System.out.println(Thread.currentThread().getName()); t.start(); } } class Tickets<Object> implements Callable<Object>{ //重写call方法 @Override public Object call() throws Exception { System.out.println(Thread.currentThread().getName()); return null; } }
优点 :可以获得返回值
start() : 作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用,真正的实现了多线程并发运行。
run() : 只是类的一个普通方法而已,直接调用run方法的话,程序中依然只有主线程这一个线程,其程序执行路径还是要顺序执行,要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到多线程的目的。
java中,每个线程都需经历新生、就绪、运行、阻塞和死亡五种状态,线程从新生到死亡的状态变化称为生命周期。
用new运算符和Thread类或其子类建立一个线程对象后,该线程就处于新生状态。
新生—>就绪:通过调用start()方法。
就绪—>运行:处于就绪状态的线程一旦得到CPU,就进入运行状态并自动调用run()方法。
运行—>阻塞:处于运行状态的线程,执行sleep()方法,或等待I/O设备资源,让出CPU并暂时中止运行,进入阻塞状态。
阻塞—>就绪:睡眠时间已到,或等待的I/O设备空闲下来,线程便进入就绪状态,重新到就绪队列中等待CPU。当再次获得CPU时,便从原来中止位置开始继续运行。
运行—>死亡:(1)线程任务完成;(2)线程被强制性的中止,如通过执行stop()或destroy()方法来终止线程。
线程终止有两种情况:线程的任务执行完成;线程在执行任务过程中发生异常。
1、使用stop()方法,已被弃用。原因是:stop()是立即终止,比较暴力,会带来数据不一致性,所以被废弃。
2、使用退出标志:实现一个Runnable接口,在其中定义volatile标志位,在run()方法中使用标志位控制程序运行,当一个变量被 volatile
修饰时,任何线程对它的写操作都会立即刷新到主内存中,并且会强制让缓存了该变量的线程中的数据清空,必须从主内存重新读取最新数据。
3、使用interrupt()中断的方式,使用interrupt()方法中断正在运行中的线程只会修改中断状态位,可以通过isInterrupted()判断。如果使用interrupt()方法中断阻塞中的线程,那么就会抛出InterruptedException异常,可以通过catch捕获异常,然后进行处理后终止线程。有些情况,我们不能判断线程的状态,所以使用interrupt()方法时一定要慎重考虑。
sleep():使当前线程暂停执行指定的一段时间,但监视状态依然保持,过了指定的时间会自行恢复运行状态。
wait():使当前线程暂停执行,同时释放对象监视器的所有权,直到另一个和它有相同对象监视器的线程调用notify()或者notifyAll()唤醒它,再恢复运行状态。
区别:(1)sleep()不会释放资源,wait()会释放资源;
(2)sleep()是Thread类里的函数,wait()是Object类里的函数;
(3)sleep()可以在任何地方调用,wait()只能在同步方法或者同步代码块中调用(否则会抛IllegalMonitorStateException异常);
Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
(1)synchronized关键字:synchronized有两种用法:synchronized方法和synchronized块
public synchronized void mutithreadAccess();
synchronized(syncObject){代码块}
当一个线程进入一个对象的synchronized()方法后,其他线程是否能够进入此对象的其他方法?
答案:其他线程可进入此对象的非synchronized修饰的方法。如果其他方法有synchronized修饰,都用的是同一对象锁,就不能访问。
如果其他方法是静态方法,且被synchronized修饰,是否可以访问?
答案:可以的,因为static修饰的方法,它用的锁是当前类的字节码,而非静态方法使用的是this,因此可以调用。
(2)Thread类的join()方法
join()方法是Thread中的一个public方法,它有几个重载版本:
1. join()
2. join(long millis) //参数为毫秒
3. join(long millis,int nanoseconds) //第一参数为毫秒,第二个参数为纳秒
(3)wait()方法和notify()方法:当使用synchronized来修饰某个共享资源时,如果线程A1执行synchronized代码,另外一个线程A2也要同时执行同一对象的同一 synchronized代码时,线程A2将要等到线程A1执行完后,才能继续执行。这种情况下可以使用wait()方法和notify()方法。
在synchronized代码被执行期间,线程可以调用对象的wait方法,释放对象锁,进入等待状态,并且可以调用notify()方法或notify()方法通知正在等待的其他线 程。notify()方法仅唤醒一个线程(等待队列中的第一个线程)并允许它去获得锁,notifyAll()方法唤醒所有等待这个对象的线程并允许它们去获得锁。
(4)Lock
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力
ReenreantLock类的常用方法有:
1. ReentrantLock() : 创建一个ReentrantLock实例
2. lock() : 获得锁
3.unlock() : 释放锁
1、编写一个有两个线程的程序,第一个线程用来计算2~100000之间的素数的个数,第二个线程用来计算100000~200000之间的素数的个数,最后输出结果。
package classtest; public class ThreadSushu extends Thread{ int i,j,x=0; ThreadSushu (int m,int n){ this.i=m; this.j=n; } public static void main(String[] args) { ThreadSushu thread1=new ThreadSushu(2,100000); ThreadSushu thread2=new ThreadSushu(100000,200000); thread1.start(); thread2.start(); } public void run(){ int p,q; p=0;q=0; for(int m=i;m<=j;m++) { for(int h=1;h<=m;h++) { q=m%h; if(q==0)p=p+1; } if(p==2) { x=x+1; } p=0; } System.out.println("输出"+i+"到"+j+"之间的质数个数:"+x); } }
2、设计2个线程,定义一个变量j,初始值为0。其中第1个线程每个循环对j增加1,循环1000次。另外1个线程对j每次减少1,循环1000次。保证线程安全,最终两个线程执行完j的值仍然是0
package classtest; public class Thread2 extends Thread{ static int j=0; static Object lock=new Object(); int flag=0; Thread2(int flag){ this.flag=flag; } public static void main(String[] args) throws InterruptedException { Thread2 thread1=new Thread2(1); Thread2 thread2=new Thread2(2); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(j); } public void run(){ for(int i=0;i<100000;i++) { synchronized(lock){ if (flag == 1) { j=j+2; } else { j=j-1; } } } } }
3、假如新建T1、T2、T3三个线程,同时启动,如何保证它们按顺序执行?也就是先执行T1,执行完后,再执行T2,执行完后,再执行 T3
package classtest; public class JoinTest { public static void main(String[] args) { Thread t1=new Thread(new Work(null)); Thread t2=new Thread(new Work(t1)); Thread t3=new Thread(new Work(t2)); t1.start(); t2.start(); t3.start(); } static class Work implements Runnable { private Thread t; public Work(Thread t) { this.t = t; } public void run() { if (t != null) { try { t.join(); System.out.println("thread start:"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } }else{ System.out.println("thread start:" + Thread.currentThread().getName()); } } } }
4、写两个线程,一个线程打印1~52,另一个线程打印A~Z,打印顺序是12A34B...5152Z;
package classtest; public class TwoThread { public static void main(String args[]){ MyObject1 my = new MyObject1(); new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub for(int i = 0; i < 26; i++){ my.printNum(); } } }).start(); new Thread(new Runnable(){ @Override public void run() { // TODO Auto-generated method stub for(int i = 0; i < 26; i++){ my.printA(); } } }).start(); } } class MyObject1{ private static boolean flag = true ; public int count = 1; public synchronized void printNum(){ while(flag == false){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.print((2*count-1)); System.out.print(2*count); flag = false; this.notify(); } public synchronized void printA(){ while(flag == true){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.print((char)(count+'A'-1)); count++; flag = true; this.notify(); } }
5、编写10个线程,第一个线程从1加到10,第二个线程从11加20…第十个线程从91加到100,最后再把10个线程结果相加。
package classtest; public class TenThread { public static class SumThread extends Thread{ int forct = 0; int sum = 0; SumThread(int forct){ this.forct = forct; } @Override public void run() { for(int i = 1; i <= 10; i++){ sum += i + forct * 10; } System.out.println(getName() + " " + sum); } } public static void main(String[] args) { int result = 0; for(int i = 0; i < 10; i++){ SumThread sumThread = new SumThread(i); sumThread.start(); try { sumThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } result = result + sumThread.sum; } System.out.println("result " + result); } }