Java-多线程入门学习

29 篇文章 0 订阅

Java-多线程入门学习

1.继承Thread类

demo01
Thread1.class

class Thread1 extends Thread{
    private String name;
    public Thread1(String name){
        this.name=name;
    }
    //一定要重写run函数
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println(name+"运行:"+i);
            try{
                sleep((int)Math.random()*10);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

主函数:

 public static void main (String args[]) {
       Thread1 thread1=new Thread1("CXY");
       Thread1 thread2=new Thread1("dhi");
       Thread1 thread3=new Thread1("DHY");
       thread1.start();
       thread2.start();
       thread3.start();
    }

Thread的一些常用方法=================

Thread.currentThread().getName();//获取线程名
Thread.currentThread().getPriority();//获取线程优先级

当子线程需要主线程的一些资源时,主线程必须比子线程晚结束,因此要使用到join

未使用join的情况=========================:

class Thread1 extends Thread{
    private String name;
    public Thread1(String name){
        super(name);
        this.name=name;
    }
    public void run(){
        System.out.println(Thread.currentThread().getName()+"线程运行开始!");
        for(int i=0;i<5;i++){
//            System.out.println(name+"运行:"+i);
            System.out.println("子线程"+name+"运行 "+i);
            try{
                sleep((int)Math.random()*10);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+"线程运行结束");
    }
      public static void main (String args[]) {
        System.out.println(Thread.currentThread().getName()+"主线程运行开始");
        Thread1 thread1=new Thread1("A");
        Thread1 thread2=new Thread1("B");
        thread1.start();
        thread2.start();
        System.out.println(Thread.currentThread().getName()+"主线程运行结束");
    }
}

结果如下:
在这里插入图片描述
很明显,子线程结束前,主线程就结束了。

使用join===============================

class Thread1 extends Thread{
    private String name;
    public Thread1(String name){
        super(name);
        this.name=name;
    }
    public void run(){
        System.out.println(Thread.currentThread().getName()+"线程运行开始!");
        for(int i=0;i<5;i++){
//            System.out.println(name+"运行:"+i);
            System.out.println("子线程"+name+"运行 "+i);
            try{
                sleep((int)Math.random()*10);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+"线程运行结束");
    }
      public static void main (String args[]) {
        System.out.println(Thread.currentThread().getName()+"主线程运行开始");
        Thread1 thread1=new Thread1("A");
        Thread1 thread2=new Thread1("B");
        thread1.start();
        thread2.start();
        try{
        	thread1.join();
        }catch(InteruptedException e){
        	e.printStackTrace();
        }
        try{
        	thread2.join();
        }catch(InteruptedException e){
        	e.printStackTrace();
        }
        
        System.out.println(Thread.currentThread().getName()+"主线程运行结束");
    }
}

结果如下:
在这里插入图片描述

yield():暂停当前正在执行的线程对象,并执行其他线程。
Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。可看上面的图。

class ThreadYield extends Thread{
    public ThreadYield(String name){
        super(name);
    }
    @Override
    public void run(){
        for(int i=1;i<=50;i++){
            System.out.println(""+this.getName()+"-----------"+i);
            //当i=30时,将CPU时间让掉,让其他线程或者自己的线程执行
            if(i==30){
                this.yield();
            }
        }
    }
    public static void main (String args[]) {
        ThreadYield yt1=new ThreadYield("张三");
        ThreadYield yt2=new ThreadYield("李四");
        yt1.start();
        yt2.start();
    }
}

结果如下:
在这里插入图片描述
在这里插入图片描述
多次测试后,我们可以发现,每次有一个线程到达30时,就会切换把CPU让给另一个线程,当也不是绝对的。

sleep和yeild的区别
在这里插入图片描述

线程数据传递方法
1.通过构造函数传递

class MyThread1 extends Thread{
    private String name;
    public MyThread1(String name){
        this.name=name;
    }
    public void run(){
        System.out.println("hello "+name);
    }
    public static void main (String args[]) {
        Thread thread=new MyThread1("world");
        thread.start();;
    }
}

因为是通过构造函数进行传递,所以在线程运行之前数据已经到位,不会造成数据在线程运行后才传入的现象。但是如果数据比较多时,可能要有多个构造函数,比较不方便。
2.通过变量和方法传递

public class MyThread2 implements Runnable   
{   
private String name;   
public void setName(String name)   
{   
this.name = name;   
}   
public void run()   
{   
System.out.println("hello " + name);   
}   
public static void main(String[] args)   
{   
MyThread2 myThread = new MyThread2();   
myThread.setName("world");   
Thread thread = new Thread(myThread);   
thread.start();   
}   
}   

建立完对象后,对对象实例逐个赋值,这样可以避免建立太多构造函数
3.通过回调函数传递数据

class Data{
    public int value=0;
}
class Work{
    public void process(Data data,Integer... numbers){
        for(int n:numbers){
            data.value+=n;
        }
    }
}
class MyThread3 extends Thread{
    private Work work;
    public MyThread3(Work work){
        this.work=work;
    }
    public void run(){
        Random random=new Random();
        Data data=new Data();
        int n1=random.nextInt(1000);
        int n2=random.nextInt(2000);
        int n3=random.nextInt(3000);
        work.process(data,n1,n2,n3);
        System.out.println(String.valueOf(n1)+"+"+String.valueOf(n2)+"+"+
                String.valueOf(n3)+"="+data.value);
    }
     public static void main (String args[]) {
        Thread thread=new MyThread3(new Work());
        thread.start();
    }
}

2.继承Runnable类

实际开发中,通常采用Runnable接口来实现多线程,优点如下:
1.避免继承的局限,一个类可以继承多个接口,但类只能继承一个类
2.Runnable接口实现的线程便于资源共享,而通过Thread类实现,各自线程(继承子类创建的线程)的资源是独立的,不方便共享

class MyThread implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println("你好,DHI");
            try{
                Thread.sleep(1000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
     public static void main (String args[]) {
        MyThread myThread=new MyThread();
        Thread thread=new Thread(myThread);
        thread.start();
    }
}

未使用同步锁时==============

class Run implements Runnable{
    private int count=1;
    @Override
    public void run(){
        for(int i=0;i<20;i++){
            System.out.println(Thread.currentThread().getName()+"运行 "+count++);
        }
    }
    public static void start(){
        Run r=new Run();
        new Thread(r,"线程A").start();
        new Thread(r,"线程B").start();
        new Thread(r,"线程C").start();
    }
    public static void main (String args[]) {
        Run.start();
    }
}

在这里插入图片描述
很明显可以看出,这个是线程不安全的,多个线程有可能在同一时间操作变量

同步锁==============

class Run implements Runnable{
    private int count=1;
    @Override
    public void run(){
        synchronized (this){
            for(int i=0;i<20;i++){
                System.out.println(Thread.currentThread().getName()+"运行 "+count++);
            }
        }
    }
    public static void start(){
        Run r=new Run();
        new Thread(r,"线程A").start();
        new Thread(r,"线程B").start();
        new Thread(r,"线程C").start();
    }
    public static void main (String args[]) {
        Run.start();
    }
}

在这里插入图片描述
很明显,这样做可以保证线程安全
在举个例子如下:
没同步锁的:

class MyThread implements Runnable{
    private static int count=0;
    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println("name="+Thread.currentThread().getName()+",count="+count++);
        }
    }
    public static void main (String args[]) {
        MyThread r1=new MyThread();
        MyThread r2=new MyThread();
        MyThread r3=new MyThread();
        Thread t1=new Thread(r1);
        Thread t2=new Thread(r2);
        Thread t3=new Thread(r3);
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述
加同步后:

class MyThread implements Runnable{
    private static int count=0;
    @Override
    public void run(){
    synchronized(this){
        for(int i=0;i<10;i++){
            System.out.println("name="+Thread.currentThread().getName()+",count="+count++);
        }
        }
    }
    public static void main (String args[]) {
        MyThread r1=new MyThread();
        MyThread r2=new MyThread();
        MyThread r3=new MyThread();
        Thread t1=new Thread(r1);
        Thread t2=new Thread(r2);
        Thread t3=new Thread(r3);
        t1.start();
        t2.start();
        t3.start();
    }
}

在这里插入图片描述
这里还是不同步的?原因是因为同步的对象不是同一个

卖票问题*

class Client implements Runnable{
    private int count=30;
    @Override
    public void run(){
        synchronized (this){
            while(count>0){
                System.out.println(Thread.currentThread().getName()+"卖出了第"+count--+"张票");
            }
        }
    }
    public static void start(){
        Client c=new Client();
        new Thread(c,"线程A").start();
        new Thread(c,"线程B").start();
        new Thread(c,"线程C").start();
    }
}
public class demo {
    public static void main (String args[]) {
        Client.start();
    }
}

在这里插入图片描述

参考连接
Java多线程学习(吐血详细整理)
java多线程入门
Java并发编程系列2.0:实现Runnable接口方式实现多线程
java多线程之:实现Runnable接口

3.获取返回值

在刚才的示例中,我们都是通过继承Thread或Runnable的,但是这两者都无法获取返回值,在一些项目中,我们需要获取线程的返回值,这时候就可以使用Callable和Future了
继承Callable后,要实现call接口

public class Main {
    static class MyThread implements Callable<Integer>{
        private int num;
        public MyThread(Integer num){
            this.num=num;
        }
        @Override
        public Integer call(){
            int result=0;
            long start=System.currentTimeMillis();
            for(int i=0;i<num;i++){
                result+=num;
                try{
                    Thread.sleep(100);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
            long end=System.currentTimeMillis();
            System.out.println("线程"+Thread.currentThread().getName()+"用时:"+(end-start));
            return result;
        }
    }
    public static void main(String[]args) {
        MyThread thread1=new MyThread(20);
        MyThread thread2=new MyThread(30);
        MyThread thread3=new MyThread(40);
        ExecutorService service= Executors.newFixedThreadPool(3);
        long start=System.currentTimeMillis();
        try{
        	//获取结果
            Future<Integer>future1=service.submit(thread1);
            Future<Integer>future2=service.submit(thread2);
            Future<Integer>future3=service.submit(thread3);
            System.out.println("future1"+future1.get());
            System.out.println("future2"+future2.get());
            System.out.println("future3"+future3.get());
        }catch (Exception e){
            e.printStackTrace();
        }
        long end=System.currentTimeMillis();
        System.out.println("用时:"+(end-start));
        service.shutdownNow();
    }
}

注意,在上面的实例中,当我们使用future.get()时,会先将对应的进行运行完,然后才会继续往下执行,这是上述代码的执行结果
在这里插入图片描述
我们将get的顺序修改一下

 public static void main(String[]args) {
        MyThread thread1=new MyThread(20);
        MyThread thread2=new MyThread(30);
        MyThread thread3=new MyThread(40);
        ExecutorService service= Executors.newFixedThreadPool(3);
        long start=System.currentTimeMillis();
        try{
            Future<Integer>future1=service.submit(thread1);
            System.out.println("future1"+future1.get());
            Future<Integer>future2=service.submit(thread2);
            System.out.println("future2"+future2.get());
            Future<Integer>future3=service.submit(thread3);
            System.out.println("future3"+future3.get());
        }catch (Exception e){
            e.printStackTrace();
        }
        long end=System.currentTimeMillis();
        System.out.println("用时:"+(end-start));
        service.shutdownNow();
    }

结果如下
在这里插入图片描述
参考文章:JAVA的Callable多线程

4.线程间通信

线程间可以通过管道来进行通信

public class Main {
    static class Producer extends Thread{
        private PipedWriter pipedWriter;
        private String content;
        public Producer(PipedWriter pipedWriter,String content){
            this.pipedWriter=pipedWriter;
            this.content=content;
        }
        @Override
        public void run(){
                    System.out.println("进入Producer");
            try{
                pipedWriter.write(content);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    static class Consumer extends Thread{
        private PipedReader pipedReader;
        public Consumer(PipedReader pipedReader){
            this.pipedReader=pipedReader;
        }
        @Override
        public void run(){
                    System.out.println("进入Consumer");
            try{
                char[]sb=new char[20];
                pipedReader.read(sb,0,sb.length);
                System.out.println("数据为:"+new String(sb));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[]args){
       PipedWriter pipedWriter=new PipedWriter();
       PipedReader pipedReader=new PipedReader();
       Producer producer=new Producer(pipedWriter,"HelloWorld----------");
       Consumer consumer=new Consumer(pipedReader);
       try{
           pipedWriter.connect(pipedReader);
           producer.start();
           consumer.start();
       }catch (IOException e){
           e.printStackTrace();
       }catch (Exception e){
           e.printStackTrace();
       }
    }
}

在这里插入图片描述
参考文章:PipedReader和PipedWriter使用详解及源码分析

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值