多线程

线程的创建和使用

方式一:继承于Thread类

start()的作用:①启动当前线程②调用当前线程的run()

注意:不能直接通过调用run()的方法启动线程,可以再创建一个线程的对象,不可以让已经start()的线程去执行。

  1. class MyThread extends Thread{
  2. public void run(){
  3. //重写的run方法
  4. }
  5. }
  6. public class thread_demo {
  7. public static void main(String[] args) {
  8. MyThread t1=new MyThread();
  9. t1.start();
  10. }
  11. }

方式二:创建Thread类的匿名子类的方式

  1. public class thread_demo {
  2. public static void main(String[] args) {
  3. new Thread(){
  4. public void run(){
  5. //重写的run方法
  6. }
  7. }.start();
  8. }
  9. }

方式三:实现Runnable接口

  1. class MyThread implements Runnable{
  2. public void run(){
  3. //重写的run方法
  4. }
  5. }
  6. public class thread_demo {
  7. public static void main(String[] args) {
  8. MyThread mythread=new MyThread();
  9. Thread t1=new Thread(mythread);
  10. t1.start();
  11. }
  12. }

方式四:JDK5.0新增 实现Callable接口

如何理解Callable比Runnable接口创建多线程方式强大

1.call()可以有返回值

2.call()可以抛出异常,被外面的操作捕获,获取异常的信息

3.call()支持泛型

  1. package callable;
  2. import java.util.concurrent.Callable;
  3. import java.util.concurrent.ExecutionException;
  4. import java.util.concurrent.FutureTask;
  5. //1.创建一个Callable的实现类
  6. class num_thread implements Callable{
  7. @Override
  8. //2.实现call方法,将此线程需要执行的操作声明在call()中
  9. public Object call() throws Exception {
  10. int sum=0;
  11. for (int i = 0; i < 20; i++) {
  12. System.out.println(i);
  13. sum+=i;
  14. }
  15. return sum;
  16. }
  17. }
  18. public class callable_interface {
  19. public static void main(String[] args) {
  20. //3.创建Callable接口实现类的对象
  21. num_thread n=new num_thread();
  22. //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
  23. FutureTask futureTask = new FutureTask(n);
  24. //5.将FutureTask的对象作为参数,传递到Thread类的构造器中,创建Thread对象,并调用start()方法
  25. new Thread(futureTask).start();
  26. try {
  27. //6.获取Callable中call()方法的返回值
  28. //get()返回值即为FutureTask构造器参数Callable实现类重写的call()返回值
  29. Object sum = futureTask.get();//get方法返回值即为FutureTask构造器参数Callable实现类重写的call()方法的返回值
  30. System.out.println(sum);
  31. } catch (InterruptedException e) {
  32. e.printStackTrace();
  33. } catch (ExecutionException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37. }

方式五:使用线程池

Thread的常用方法

  1. start():启动当前线程,调用当前线程的run()。
  2. run():通常需要重写Thread类中的此方法,将创建的线程需要执行的操作声明在此方法中。
  3. currentThread():静态方法,返回执行当前代码的线程。
  4. getName():获取当前线程的名字。
  5. setName():设置当前线程的名字。
  6. yield():释放当前CPU的执行权。
  7. join():在线程a中调用线程bjoin(),此时线程a就进入阻塞状态,知道线程b完全执行完以后,线程a才结束阻塞状态。
  8. stop():已过时,当执行次方法时,强制结束当前线程。
  9. sleep(long millitime):让当前线程睡眠指定的毫秒。

同步

同步机制,解决线程安全问题

说明:

1.操作共享数据的代码,即为需要被同步的代码.(不能包含代码多了,也不能包含代码少了)

2.有共享数据才有线程安全问题,共享数据:多个线程共同操作的变量。

3.同步监视器,俗称:锁。任何一个类的对象都可以充当锁。

要求:多个线程必须共用同一把锁。

补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。

解决线程安全的方法

方式一:同步代码块

synchronized(同步监视器){

//需要被同步的代码

}

  1. eg1:[用构造的对象当锁] main函数造了三个 window1 对象,构造obj对象时需要用static;
  2. class window1 extends Thread{
  3. private static int ticket=100;
  4. private static Object obj=new Object();
  5. public void run(){
  6. whlie(true){
  7. synchronized(obj){
  8. if(ticket>0){
  9. try{
  10. Thread.sleep(100);
  11. }catch(InterruptedException e){
  12. e.printStackTrace();
  13. }
  14. System.out.println(getName()+":"+ticket);
  15. ticket--;
  16. }else{
  17. break;
  18. }
  19. }
  20. }
  21. }
  22. }
  23. public class demo1 {
  24. public static void main(String[] args) {
  25. window1 t1=new window1();
  26. window1 t2=new window1();
  27. window1 t3=new window1();
  28. t1.setName("窗口1");
  29. t2.setName("窗口2");
  30. t3.setName("窗口3");
  31. t1.start();
  32. t2.start();
  33. t3.start();
  34. }
  35. }
  1. eg2:[this当锁] main函数只构造了一个 window2 对象,锁可以用this,此时的this是唯一的window2对象;
  2. class window2 extends Thread{
  3. private static int ticket=100;
  4. public void run(){
  5. whlie(true){
  6. synchronized(this){
  7. if(ticket>0){
  8. try{
  9. Thread.sleep(100);
  10. }catch(InterruptedException e){
  11. e.printStackTrace();
  12. }
  13. System.out.println(getName()+":"+ticket);
  14. ticket--;
  15. }else{
  16. break;
  17. }
  18. }
  19. }
  20. }
  21. }
  22. public class demo2 {
  23. public static void main(String[] args) {
  24. window2 w=new window2();
  25. Thread t1=new Thread(w);
  26. Thread t2=new Thread(w);
  27. Thread t3=new Thread(w);
  28. t1.setName("窗口1");
  29. t2.setName("窗口2");
  30. t3.setName("窗口3");
  31. t1.start();
  32. t2.start();
  33. t3.start();
  34. }
  35. }
  1. eg3:[类当锁] 类也是对象(反射)Class clazz=window3.class
  2. class window3 extends Thread{
  3. private static int ticket=100;
  4. private static Object obj=new Object();
  5. public void run(){
  6. whlie(true){
  7. synchronized(window3.class){
  8. if(ticket>0){
  9. try{
  10. Thread.sleep(100);
  11. }catch(InterruptedException e){
  12. e.printStackTrace();
  13. }
  14. System.out.println(getName()+":"+ticket);
  15. ticket--;
  16. }else{
  17. break;
  18. }
  19. }
  20. }
  21. }
  22. }
  23. public class demo3 {
  24. public static void main(String[] args) {
  25. window3 w=new window3();
  26. Thread t1=new Thread(w);
  27. Thread t2=new Thread(w);
  28. Thread t3=new Thread(w);
  29. t1.setName("窗口1");
  30. t2.setName("窗口2");
  31. t3.setName("窗口3");
  32. t1.start();
  33. t2.start();
  34. t3.start();
  35. }
  36. }

方式二:同步方法

关于同步方法的总结:

1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。

2.非静态的同步方法,同步监视器是:this

静态的同步方法,同步监视器是:当前类本身

  1. eg4:
  2. class window4 extends Thread{
  3. private static int ticket=100;
  4. public void run(){
  5. while(true){
  6. show();
  7. }
  8. }
  9. }
  10. private synchronized void show(){
  11. if(ticket>0){
  12. try{
  13. Thread.sleep(100);
  14. }catch(InterruptedException e){
  15. e.printStackTrace();
  16. }
  17. System.out.println(getName()+":"+ticket);
  18. ticket--;
  19. }
  20. }
  21. public class demo3 {
  22. public static void main(String[] args) {
  23. window3 w=new window3();
  24. Thread t1=new Thread(w);
  25. Thread t2=new Thread(w);
  26. Thread t3=new Thread(w);
  27. t1.setName("窗口1");
  28. t2.setName("窗口2");
  29. t3.setName("窗口3");
  30. t1.start();
  31. t2.start();
  32. t3.start();
  33. }
  34. }
  1. eg5:
  2. class window5 implemens Runnable{
  3. private int ticket=100;
  4. public void run(){
  5. while(true){
  6. show();
  7. }
  8. }
  9. }
  10. private static synchronized void show(){
  11. if(ticket>0){
  12. try{
  13. Thread.sleep(100);
  14. }catch(InterruptedException e){
  15. e.printStackTrace();
  16. } System.out.println(Thread.currentThread().getName()+":"+ticket);
  17. ticket--;
  18. }
  19. }
  20. public class demo3 {
  21. public static void main(String[] args) {
  22. window5 t1=new window5();
  23. window5 t2=new window5();
  24. window5 t3=new window5();
  25. t1.setName("窗口1");
  26. t2.setName("窗口2");
  27. t3.setName("窗口3");
  28. t1.start();
  29. t2.start();
  30. t3.start();
  31. }
  32. }

方式三:Lock锁

  1. JDK 5.0新增
  2. class window extends Thread{
  3. private static int ticket=100;
  4. private ReentrantLock lock=new ReentrantLock();//1.实例化ReentrantLock
  5. public void run(){
  6. whlie(true){
  7. try{
  8. lock.lock();//2.调用lock()方法
  9. if(ticket>0){
  10. try{
  11. Thread.sleep(100);
  12. }catch(InterruptedException e){
  13. e.printStackTrace();
  14. }
  15. System.out.println(getName()+":"+ticket);
  16. ticket--;
  17. }else{
  18. break;
  19. }
  20. }finally{
  21. lock.unlock();//3.调用解锁方法unlock()
  22. }
  23. }
  24. }
  25. }
  26. public class demo3 {
  27. public static void main(String[] args) {
  28. window3 w=new window3();
  29. Thread t1=new Thread(w);
  30. Thread t2=new Thread(w);
  31. Thread t3=new Thread(w);
  32. t1.setName("窗口1");
  33. t2.setName("窗口2");
  34. t3.setName("窗口3");
  35. t1.start();
  36. t2.start();
  37. t3.start();
  38. }
  39. }

synchronized与Lock的异同

1.相同:都可以解决线程安全问题

2.不同:synchronized机制在执行完相应的同步代码块以后,自动释放同步监视器

Lock需要手动的启动同步(Lock()),同时结束同步也需要手动的实现(unlock());

  1. 优先使用顺序:Lock->同步代码块(已经进入了方法体,分配了相应资源)->同步方法(在方法体之外)

例题:两个储户分别向同一个账户存3000元,每次存1000,存三次
  1. package test;
  2. class account{
  3. private double balance;
  4. public account(double balance){
  5. this.balance=balance;
  6. }
  7. public synchronized void deposit(double m) {
  8. if (m > 0) {
  9. balance += m;
  10. try {
  11. Thread.sleep(1000);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }
  15. System.out.println(Thread.currentThread().getName() + "存钱成功,余额为:" + balance);
  16. }
  17. }
  18. }
  19. class customer extends Thread{
  20. private account a;
  21. public customer(account a){
  22. this.a=a;
  23. }
  24. @Override
  25. public void run() {
  26. for (int i = 0; i < 3; i++) {
  27. a.deposit(1000);
  28. }
  29. }
  30. }
  31. public class account_test {
  32. public static void main(String[] args) {
  33. account a=new account(0);
  34. customer c1=new customer(a);
  35. customer c2=new customer(a);
  36. c1.setName("甲");
  37. c2.setName("乙");
  38. c1.start();
  39. c2.start();
  40. }
  41. }