JAVA基础-多线程
Posted 阿拉巴洞
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA基础-多线程相关的知识,希望对你有一定的参考价值。
一、线程与进程
进程:正在计算机中运行的一个程序,当一个程序进入内存运行,即变成一个进程。一个软件至少有一个进程,
有的软件在点击一个应用图标是可能会给你开多个进程,如:360全家桶。
线程:软件在计算机中执行的一条路径。软件可以是多线程的。
总结:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。而多线程就是一个程序中有多个线程
在同时执行。而JAVA为我们提供了2种方式实现多线程:1、继承Thread类 2、实现Runnable接口。我们在编写程
序时的主方法就是一个单独的线程,线程名叫 "main"。而JAVA虚拟机Jvm运行是多线程运行。
二、Thread类
JAVA实现多线程其中的一种方法就是继承Thread类,Thread是程序中的执行线程。Java 虚拟机允许应用程序并
发地运行多个执行线程。Thread类常用的构造方法有:1、分配新的 Thread 对象 - Thread(),2、分配新的Thread
对象,将指定的name作为线程名称 - Thread(String name)。其常用的方法有:
1、改变线程名称,使之与参数 name 相同:setName(String name)
2、该线程要执行的操作:run()
3、在指定的毫秒数内让当前正在执行的线程休眠(暂停执行):sleep()
4、使该线程开始执行;Java 虚拟机调用该线程的 run 方法:start()
5、返回该线程的名称:getName()
6、静态方法,返回值为Thread对象 - 返回对当前正在执行的线程对象的引用:currentThread()
A:创建线程的步骤:
1.定义一个类继承Thread。
2.重写run方法。
3.创建子类对象(创建线程对象)。
4.调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。
举例:
1 //定义类MyThread继承Thread类 2 public class MyThread extends Thread { 3 @Override 4 // 重写Thread类里的run方法 5 public void run() { 6 // 想要多线程执行的代码 7 for (int i = 0; i < 100; i++) { 8 // 获取线程名称和循环的次数 9 System.out.println(getName() + " " + i); 10 } 11 } 12 }
主函数:
1 public static void main(String[] args) { 2 // 创建线程一 3 MyThread myThread01 = new MyThread(); 4 // 为线程一设置线程名 5 myThread01.setName("线程一"); 6 // 开启线程一 7 myThread01.start(); 8 // 创建线程二 9 MyThread myThread02 = new MyThread(); 10 // 为线程二设置线程名 11 myThread02.setName("线程二"); 12 // 开启线程二 13 myThread02.start(); 14 }
三、Runnable接口
JAVA实现多线程的另一种方法就是实现Runnable接口,实现 run 方法。然后创建Runnable的子类对象,
传入到某个线程的构造方法中,开启线程。Runnable接口其实就是用来指定每个线程要执行的任务,在由线
程类Thread来执行这些任务。Runnable接口只有一个要实现的方法:run(),作用与Thread类的run方法类似,
在启动该线程时将被调用,也就是说将由其实现类来实现run方法。Runnable接口的常用实现类是Thread类。
B:创建线程的步骤:
1、定义类实现Runnable接口。
2、覆盖接口中的run方法。
3、创建Thread类的对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
5、调用Thread类的start方法开启线程。
举例:
1 //创建MyRunable类实现Runnable接口 2 public class MyRunable implements Runnable { 3 @Override 4 //重写run方法 5 public void run() { 6 /*循环 7 Thread.currentThread().getName: 8 通过Thread类的静态方法currentThread()方法,返回正在执行的线程对象,并获取线程名*/ 9 for (int i = 0; i < 100; i++) { 10 System.out.println(Thread.currentThread().getName() + " :" + i); 11 } 12 } 13 }
*主方法:
1 public static void main(String[] args) { 2 // 获取MyRunable对象(线程任务) 3 MyRunable r = new MyRunable(); 4 // 根据线程任务创建线程一 5 Thread t1 = new Thread(r); 6 // 命名 7 t1.setName("线程一"); 8 // 开启线程 9 t1.start(); 10 // 根据线程任务创建线程二 11 Thread t2 = new Thread(r); 12 t2.setName("线程二"); 13 t2.start(); 14 }
四、多线程安全
由于CUP对线程的处理具有随机性,这就导致了当线程1进入某一循环,还没有执行循环里的语句时,
CUP突然切换到线程2,线程2也进入了循环语句。这样就会导致本应该只执行一次的条件执行了2次,导致
线程不安全的问题出现。
问题出现的原因:
* 要有多个线程
* 要有被多个线程所共享的数据
* 多个线程并发的访问共享的数据。
用实现Runnable接口的方式对同一数据进行
操作可能就会出现这种重复的问题:
(一)、多线程安全的解决
问题出现了该如何解决呢?多线程处理的机制其实和我们在火车上厕所类似。在火车上有好多人都要去厕所,
但是厕所只能供一个人使用,2个人如果一起挤进了厕所就会发生一些“事情”,那么火车是怎么解决这个问题的呢?
我们可以在厕所的门上上一把锁,路人甲进厕所后把门锁起来,方便完之后再把锁打开,路人乙在进去把门锁上。
而JAVA为了解决多线程的并发安全问题,也给我们提供了这么一把“锁” - 同步代码块和同步方法。
1、同步代码块:
synchronized(锁对象){
}
举例:
1 public class MyRunable implements Runnable { 2 // 设置一个被操作的数据 3 int i = 100; 4 5 @Override 6 public void run() { 7 while (true) { 8 // 设置同步代码块 9 synchronized (MyRunable.class) { 10 try { 11 // 为了不要太快,先睡一会 12 Thread.sleep(200); 13 } catch (InterruptedException e) { 14 // TODO Auto-generated catch block 15 e.printStackTrace(); 16 } 17 // 对数据进行操作 18 if (i > 0) { 19 System.out.println(Thread.currentThread().getName() + " " + i--); 20 } 21 } 22 } 23 } 24 }
*入口方法:
1 public static void main(String[] args) { 2 MyRunable runable = new MyRunable(); 3 Thread t1 = new Thread(runable); 4 t1.setName("线程A"); 5 Thread t2 = new Thread(runable); 6 t2.setName("线程B"); 7 Thread t3 = new Thread(runable); 8 t3.setName("线程C"); 9 t1.start(); 10 t2.start(); 11 t3.start(); 12 }
锁对象:可以是java中的任意对象。多个同步代码块必须使用同一个对象,才能实现多个代码块之间的同步。一
般使用当前类的.class对象
2、同步方法:
非静态方法:this
静态方法:字节码对象(类名.class)
举例:
1 public class MyRunable implements Runnable { 2 // 设置一个被操作的数据 3 int i = 100; 4 5 public void run() { 6 while (true) { 7 method(); 8 } 9 } 10 11 private synchronized void method() { 12 13 try { 14 Thread.sleep(150); 15 } catch (InterruptedException e) { 16 // TODO Auto-generated catch block 17 e.printStackTrace(); 18 } 19 if (i > 0) { 20 System.out.println(Thread.currentThread().getName() + " :" + i); 21 i--; 22 }else { 23 System.exit(0); 24 } 25 }
*入口方法:
1 public static void main(String[] args) { 2 MyRunable runable = new MyRunable(); 3 Thread t1 = new Thread(runable); 4 t1.setName("线程A"); 5 Thread t2 = new Thread(runable); 6 t2.setName("线程B"); 7 Thread t3 = new Thread(runable); 8 t3.setName("线程C"); 9 t1.start(); 10 t2.start(); 11 t3.start(); 12 }
3、优缺点
* 同步:安全性高,效率低。
* 非同步:效率高,但是安全性低。
五、使用多线程对同一数据进行不同的操作
代码:
1 public class Demo { 2 public static class AddSub { 3 //提供原子操作的Integer的类(无锁的线程安全整数 AtomicInteger) 4 AtomicInteger at = new AtomicInteger(1); 5 int j = 1; 6 7 public static void main(String[] args) { 8 AddSub ab = new AddSub(); 9 Add a = ab.new Add(); 10 Sub b = ab.new Sub(); 11 Thread t1 = new Thread(a); 12 Thread t2 = new Thread(a); 13 Thread t3 = new Thread(b); 14 Thread t4 = new Thread(b); 15 t1.start(); 16 t2.start(); 17 t3.start(); 18 t4.start(); 19 } 20 21 public synchronized void add() { 22 j++; 23 System.out.println("add:" + j); 24 } 25 26 public synchronized void sub() { 27 j--; 28 System.out.println("sub:" + j); 29 } 30 31 class Add implements Runnable { 32 @Override 33 public void run() { 34 for (int i = 0; i < 20; i++) { 35 add(); 36 } 37 38 } 39 } 40 41 class Sub implements Runnable { 42 @Override 43 public void run() { 44 for (int i = 0; i < 20; i++) { 45 sub(); 46 } 47 48 } 49 } 50 } 51 }
以上是关于JAVA基础-多线程的主要内容,如果未能解决你的问题,请参考以下文章