多线程和线程安全
Posted quanjunkang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程和线程安全相关的知识,希望对你有一定的参考价值。
1 多线程介绍
1.进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
2.线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
3.分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
4. 抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
jvm启动后,必然有一个执行路径(线程)从main方法开始的,一直执行到main方法结束,这个线程在java中称之为主线程。
2 创建线程
2.1 创建线程方式一继承Thread类
将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法。
public class MyThread extends Thread{ public void run() { for(int i=0;i<100;i++){ System.out.println("run...."+i); } } } public class Demo01 { public static void main(String[] args) { //创建线程 MyThread mt=new MyThread(); //开启线程 mt.start(); for(int i=0;i<100;i++){ System.out.println("main...."+i); } //静态方法Thread.currentThread()返回当前正在运行的线程对象,getName()获得线程名 System.out.println(Thread.currentThread().getName()); } }
public class Demo02 { public static void main(String[] args) throws InterruptedException { for(int i=0;i<50;i++){ System.out.println(i); //休眠5000毫秒 Thread.sleep(5000); } } }
2.2 创建线程方式—实现Runnable接口
该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。
public class MyRunnable implements Runnable{ public void run() { for(int i=0;i<50;i++){ System.out.println("run..."+i); } } }
public class Demo01 { public static void main(String[] args) { //创建线程任务对象 MyRunnable mr=new MyRunnable(); //创建线程对象 Thread thread=new Thread(mr); //开启线程 thread.start(); for(int i=0;i<50;i++){ System.out.println("main..."+i); } } }
3 匿名内部类创建线程
public class Demo02 { public static void main(String[] args) { //创建线程子类对象 Thread th=new Thread(){ public void run(){ System.out.println(Thread.currentThread().getName()); }; }; //开启线程 th.start(); //创建线程任务对象 Runnable r=new Runnable() { @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"run"); } }; //创建线程对象 Thread th1=new Thread(r); //开启线程 th1.start(); } }
4 线程池
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
l 使用线程池中线程对象的步骤:
n 创建线程池对象
n 创建Runnable接口子类对象
n 提交Runnable接口子类对象
n 关闭线程池
public class Demo03 { public static void main(String[] args) { //Executors 线程池工厂类 //ExecutorService 线程池 //获取线程池对象 ExecutorService es=Executors.newFixedThreadPool(2); //创建线程任务对象 MyRunnable mr=new MyRunnable(); //执行线程任务 es.submit(mr); es.submit(mr); es.submit(mr); //释放资源 es.shutdown(); } }
l Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
public class MyCallable implements Callable<String>{ @Override public String call() throws Exception { // TODO Auto-generated method stub return "abc"; } }
public class Demo04 { public static void main(String[] args) throws InterruptedException, ExecutionException { //创建线程任务 MyCallable mc=new MyCallable(); //获取线程池工厂 ExecutorService es=Executors.newFixedThreadPool(2); //执行线程任务 Future<String>f=es.submit(mc); //获取返回值r String str=f.get(); System.out.println(str); //关闭线程池 es.shutdown(); } }
5 线程池练习:返回两个数相加的结果
public class MyCallable implements Callable<Integer>{ private int num; public MyCallable(int num){ this.num=num; } public Integer call() throws Exception { int sum=0; for(int i=0;i<=num;i++){ sum+=i; } return sum; } } public class Demo01 { //用两个线程分别计算1..100的和 //1...200的和 public static void main(String[] args) throws InterruptedException, ExecutionException { //创建两个线程任务 MyCallable mc1=new MyCallable(100); MyCallable mc2=new MyCallable(200); ExecutorService es=Executors.newFixedThreadPool(2); //执行线程任务 Future<Integer>sum1=es.submit(mc1); Future<Integer>sum2=es.submit(mc2); //获取返回值 int s1=sum1.get(); int s2=sum2.get(); System.out.println(s1); System.out.println(s2); //释放资源 es.shutdown(); } }
6 线程安全
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
练习:多线程模拟电影院卖票
6.1 同步代码块
同步代码块: 在代码块声明上 加上synchronized
6.2 同步方法
l 同步方法:在方法声明上加上synchronized
同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。
public class MyRunnable implements Runnable{ //卖电影票 private int ticket=100; private Object obj=new Object(); private Lock lock=new ReentrantLock(); public void run(){ while(true){ synchronized (obj) { if(ticket>0){ try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"出售地"+ticket--+"张票"); } } } } public synchronized void sale(){ if(ticket>0){ try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"出售地"+ticket--+"张票"); } } } public class Demo01 { public static void main(String[] args) { MyRunnable mr=new MyRunnable(); Thread t0=new Thread(mr); Thread t1=new Thread(mr); Thread t2=new Thread(mr); //开启线程 t0.start(); t1.start(); t2.start(); } }
以上是关于多线程和线程安全的主要内容,如果未能解决你的问题,请参考以下文章