java多线程基础
来源:互联网 发布:ping用到的网络协议 编辑:程序博客网 时间:2024/04/27 15:31
1.多线程的创建
多线程创建的方式有两种:
1.通过继承Thread类来实现多线程
2.通过实现Runnable接口来实现多线程
下面我们分别介绍这两种多线程的创建方式
下面我们来看这两种实现方式:
package gt;/** * Created by Cronous on 2017/10/30. * 创建线程的第一种方式继承Thread类 */public class day02 { /* Thread类用于描述线程,线程是需要任务的,所以Thread类也对任务的描述 这个任务就是Thread类中的run方法,也就是说,run方法就是封装自定义线程 运行任务的函数 run方法中定义的就是线程要运行的任务代码 */ public static void main(String[] args){ Demo2 d1 = new Demo2("旺财"); Demo2 d2 = new Demo2("小强"); ////d1.start(); ///d2.start(); Demo1 demo1 = new Demo1("tom"); Demo1 demo2 = new Demo1("jerry"); Thread t1 = new Thread(demo1); Thread t2 = new Thread(demo2); t1.start(); t2.start(); }}/*第一种方式 1.定义一个类继承Thread类 2.覆盖Thread类中的run方法 3.直接创建Thread类的子类对象 4.调用 start()方法并调用线程的任务第一种方式有个弊端,如果这个类已经有父类了,就存在这个这个问题了 可以通过 Thread.getName()获取线程名称 我们来看第二种方式,实现 Runnable接口 1.实现接口 2.创建Thread对象,然后将new的对象放入 Thread中 让线程对象调用*/class Demo2 extends Thread{ private String name; Demo2(String name){ super(name); } @Override public void run() { show(); } public void show(){ for(int i = 0;i < 10;i++){ System.out.println(name + "...i=" + i + " "+ Thread.currentThread()); } }}//第二种方式 实现 Runnable接口class Demo1 implements Runnable{ private String name; Demo1(String name){ this.name = name; } @Override public void run() { show(); } public void show(){ for(int i = 0;i < 10;i++){ System.out.println(name + "...i=" + i + " "+ Thread.currentThread()); } }}
这里我们可以发现:如果某个类已经有父类了,我们就无法继承Thread类,
这时就存在这个问题了,我们需要使用实现Runnable接口
1.我们需要把覆盖run方法
2.将需要多线程执行的代码放入run函数中
3.如果是继承Thread类直接可以new线程类,然后调用start()方法
4.如果是实现Runnable接口,则new Runnable对象,使用Thread构造方法
Thread(Runnable r)
进行构造Thread线程对象,之后调用start()
方法
控制台结果:
tom...i=0 Thread[Thread-0,5,main]
tom...i=1 Thread[Thread-0,5,main]
tom...i=2 Thread[Thread-0,5,main]
tom...i=3 Thread[Thread-0,5,main]
tom...i=4 Thread[Thread-0,5,main]
tom...i=5 Thread[Thread-0,5,main]
tom...i=6 Thread[Thread-0,5,main]
tom...i=7 Thread[Thread-0,5,main]
tom...i=8 Thread[Thread-0,5,main]
tom...i=9 Thread[Thread-0,5,main]
jerry...i=0 Thread[Thread-1,5,main]
jerry...i=1 Thread[Thread-1,5,main]
jerry...i=2 Thread[Thread-1,5,main]
jerry...i=3 Thread[Thread-1,5,main]
jerry...i=4 Thread[Thread-1,5,main]
jerry...i=5 Thread[Thread-1,5,main]
jerry...i=6 Thread[Thread-1,5,main]
jerry...i=7 Thread[Thread-1,5,main]
jerry...i=8 Thread[Thread-1,5,main]
jerry...i=9 Thread[Thread-1,5,main]
2.多线程的安全问题
我们看下面的一个卖票案例,有一百张票,多个线程同时进行售票
class day02{ public static void main(String[] args){ Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); Ticket t3 = new Ticket(); Ticket t4 = new Ticket(); t1.start(); t2.start(); t3.start(); t4.start(); }}class Ticket extends Thread{ private int num = 100; public void run(){ while(true){ if(num > 0){ try{ }catch(InterruptedException e) {} System.out.println(Thread.currentThread().getName()+ "....sale..."+ num--); } } }}
控制台会有这样的结果:
Thread-2....sale... 1Thread-1....sale... 1
我们发现两个线程居然重复进行了1号票的出售,这种结果是有问题的
线程产生问题的原因:
1.多个线程在操作共享数据
2.操作共享数据的线程代码有多条,如上面的代码 if(num > 0)是一条,
System.ou.println()输出又是一条,线程1可能执行到第一条语句就停在那儿了,并没有输出num,但是第二个线程可能就会执行到输出,所以导致原先应该是线程1输出的num被线程2输出,并且因为线程1保留着状态,所以等他得到执行权还是会输出原先的num,这样就导致了重复num的出现,从而导致安全问题
3.安全问题的解决
3.1 同步代码块
解决思路:
将多条操作共享的线程代码封装起来,当有线程在执行这些代码的时候,
其他的线程是不可以参与运算的,必须等待当前线程执行完毕后,其它线程才可以执行
同步代码块可以解决这个问题:
格式:synchronized(对象)
{
//需要被同步的代码块
}
class day02{ public static void main(String[] args){ Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); Ticket t3 = new Ticket(); Ticket t4 = new Ticket(); t1.start(); t2.start(); t3.start(); t4.start(); }}class Ticket extends Thread{ private int num = 100; Object obj = new Object(); public void run(){ while(true){ synchronized(obj){ if(num > 0){ try{ }catch(InterruptedException e) {} System.out.println(Thread.currentThread().getName()+ "....sale..."+ num--); } } } }}
这样问题即可解决
同步代码块的好处与弊端:
好处:解决了线程安全问题
坏处:一定程度上影响了执行效率,因为同步外的线程都会判断同步锁,这里的锁即同步中的对象
同步的前提:必须有多个线程并使用同一个锁
我们看一个不是使用一个锁的情况,很简单,只需要将Object obj = new Object()放在执行函数run里面即可,这样每次new线程对象的时候obj就不是同一个对象,这就是不同的锁,修改代码如下:
class day02{ public static void main(String[] args){ Ticket t1 = new Ticket(); Ticket t2 = new Ticket(); Ticket t3 = new Ticket(); Ticket t4 = new Ticket(); t1.start(); t2.start(); t3.start(); t4.start(); }}class Ticket extends Thread{ private int num = 100; //Object obj = new Object(); public void run(){ Object obj = new Object(); while(true){ synchronized(obj){ if(num > 0){ try{ }catch(InterruptedException e) {} System.out.println(Thread.currentThread().getName()+ "....sale..."+ num--); } } } }}
控制台打印:
Thread-2....sale... 1Thread-1....sale... 0Thread-0....sale... -2
出现了负数票,不合常理,出现错误,原因:用的不是同一个锁
3.2 同步函数
我们看一个银行的例子:储户向银行存钱行为
package gt;/** * Created by Cronous on 2017/10/31. * * 这里我们可以使用同步函数 同步代码块在函数内,所以可以将函数进行同步 */public class BankDemo { public static void main(String[] args){ Cus c = new Cus(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); }}class Bank{ private int sum; //private Object obj = new Object(); public void add(int num){ sum = sum + num;//这里代码会出并发问题 System.out.println("sum=" + sum); }}class Cus implements Runnable{ Bank b = new Bank(); @Override public void run() { //Bank b = new Bank();这样的意思是两储户是去两银行存钱 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } for(int x = 0;x < 10;x++){ b.add(100); } }}
控制台打印:
......sum=1700sum=1400sum=1800sum=1900
居然显示了1900,正确应该是2000
我们会发现出问题的代码在函数内部,函数本身也是一种封装,所以我们可以让函数具备同步性
这里我们可以使用另一种方式:同步函数的方式来解决
class Bank{ private int sum; //private Object obj = new Object(); public synchronized void add(int num){ sum = sum + num;//这里代码会出并发问题 System.out.println("sum=" + sum); }}
只需要在函数前加上修饰符 synchronized
这里我们就有个问题,同步代码块的锁是对象,那么同步函数的锁是谁?
我们继续卖票例子:
public class tikets implements Runnable{ private static int num = 100; Object obj = new Object(); boolean flag = true; @Override public void run(){ System.out.println("this" + this); if(flag){ while(true){ synchronized(obj){ if(num > 0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(num-- + "....." + Thread.currentThread()); } } } }else{ while(true){ show(); } } } public synchronized void show(){ if(num > 0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(num-- + ".....function" + Thread.currentThread()); } }}class test{ public static void main(String[] args){ //System.out.println("Hello,world!"); tikets t1 = new tikets(); Thread thread1 = new Thread(t1); Thread thread2 = new Thread(t1); thread1.start(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } t1.flag = false; thread2.start(); }}
设置一个flag,为true执行同步代码块,false执行同步函数
控制台打印结果如下:
......5.....Thread[Thread-0,5,main]4.....functionThread[Thread-1,5,main]2.....functionThread[Thread-1,5,main]3.....Thread[Thread-0,5,main]1.....functionThread[Thread-1,5,main]1.....Thread[Thread-0,5,main]
我们会发现Thread-1
与Thread-2
都卖了1号票,说明同步代码块和同步函数所使用的锁是不一样的
函数是被对象调用的,所以函数持有对象this,所以同步函数的锁即调用它的对象
1.同步函数的锁是固定的this
2.同步代码块锁是任意的对象
建议使用同步代码块
那么静态同步函数的锁是谁?—静态函数没有this
分析:静态函数是随着类的加载而加载,java对象建立都有自己的字节码文件对象,所以静态函数的锁即是它的类对象 synchronized(this.getClass()),如果在静态方法中则为synchronized(Xxxx.class)
下面的show()方法静态修饰了,我们看同步代码块如何修改的
public class tikets implements Runnable{ private static int num = 100; Object obj = new Object(); boolean flag = true; @Override public void run(){ System.out.println("this" + this); if(flag){ while(true){ //这里做修改this.getClass synchronized(this.getClass()){ if(num > 0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(num-- + "....." + Thread.currentThread()); } } } }else{ while(true){ show(); } } } public static synchronized void show(){ if(num > 0){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(num-- + ".....function" + Thread.currentThread()); } }}class test{ public static void main(String[] args){ //System.out.println("Hello,world!"); tikets t1 = new tikets(); Thread thread1 = new Thread(t1); Thread thread2 = new Thread(t1); thread1.start(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } t1.flag = false; thread2.start(); }}
- Java基础/Java多线程
- Java基础-多线程基础篇
- java多线程基础
- Java多线程编程基础
- java多线程开发基础
- Java多线程基础
- Java -- 多线程技术基础
- 【java】多线程基础
- Java基础:多线程
- Java语言基础:多线程
- Java语言基础:多线程
- Java语言基础:多线程
- java多线程基础分析
- Java多线程编程基础
- java 多线程基础
- Java基础_多线程
- Java多线程基础
- java多线程基础
- 58. Length of Last Word
- 第7章任务2:对数据的异常处理
- Fresco加载网络图片的使用
- 浅入浅出UML之类图
- Python入门小游戏
- java多线程基础
- uml通信图
- python 的容器之列表使用
- Java消息中间件学习笔记四 -- ActiveMQ的使用,【队列模式】
- Android --- WebView -- 混合开发(二)
- Java Integer与int的区别
- jvm terminated.exit code=-1
- 事件抽取--论元
- Android 源码系列之<一>从源码的角度深入理解ImageView的ScaleType属性