欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java多线程相关代码

程序员文章站 2022-12-15 15:57:48
1.创建线程的三种方式 使用Thread 使用Runnable 使用Callable接口创建的线程会获得一个返回值并且可以声明异常。 优点: 可以获取返回值 可以抛出异常 线程池 线程池是初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时直接去这个线程集合中获取,而不是创建一个 ......

1.创建线程的三种方式

  使用thread

package com.wpbxx.test;

//1.自定义一个类,继承java.lang包下的thread类
class mythread extends thread{
    //2.重写run方法
    @override
    public void run() {
        //3.将要在线程中执行的代码编写在run方法中
        for(int i = 0; i < 1000; i++) {
            system.out.println("wpb");
        }
    }
}
public class helloworld {

    public static void main(string[] args) {
        //4.创建上面自定义类的对象
        mythread mt  = new mythread();
        //5.调用start方法启动线程
        mt.start();
        for(int i = 0; i< 1000; i++) {
            system.out.println("xx");
        }
    }

}

使用runnable

package com.wpbxx.test;

//1.自定义一个类实现java.lang包下的runnable接口
class myrunnable implements runnable{
    //2.重写run方法
        @override
    public void run() {
            //3.将要在线程中执行的代码编写在run方法中
        for(int i = 0; i < 1000; i++) {
            system.out.println("wpb");
        }
    }
}
public class helloworld {

    public static void main(string[] args) {
        //4.创建上面自定义类的对象
        myrunnable mr = new myrunnable();
        //5.创建thread对象并将上面自定义类的对象作为参数传递给thread的构造方法
        thread t = new thread(mr);
        //6.调用start方法启动线程
        t.start();
        for(int i = 0; i < 1000; i++) {
            system.out.println("xx");
        }
    }

}

使用callable接口创建的线程会获得一个返回值并且可以声明异常。

优点: 可以获取返回值 可以抛出异常

线程池

线程池是初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时直接去这个线程集合中获取,而不是创建一个线程。任务执行结束后,线程回到池子中等待下一次的分配。

线程池的作用
解决创建单个线程耗费时间和资源的问题。

package com.wpbxx.test;

import java.util.concurrent.callable;
import java.util.concurrent.executionexception;
import java.util.concurrent.executorservice;
import java.util.concurrent.executors;
import java.util.concurrent.future;
//1.自定义一个类实现java.util.concurrent包下的callable接口
class mycallable implements callable<integer>{
    private int count;
    public mycallable(int count) {
        this.count = count;
    }
    //2.重写call方法
        @override
    public integer call() throws exception{
            //3.将要在线程中执行的代码编写在call方法中
        for(int i = 0; i < 100; i++) {
            count++;
        }
        return count;
    }
}
public class helloworld {

    public static void main(string[] args) {
        //4.创建executorservice线程池 里面为线程的数量
        executorservice es = executors.newfixedthreadpool(2);
        ////创建一个线程池,里面的线程会根据任务数量进行添加
        //executorservice es = executors.newcachedthreadpool();
    
        //5.将自定义类的对象放入线程池里面
        future<integer> f1= es.submit(new mycallable(5));
        future<integer> f2 = es.submit(new mycallable(3));
        try {
            //6.获取线程的返回结果
            system.out.println(f1.get());
            system.out.println(f2.get());
            
        } catch (interruptedexception e) {
            // todo auto-generated catch block
            e.printstacktrace();
        } catch (executionexception e) {
            // todo auto-generated catch block
            e.printstacktrace();
        }
        //7.关闭线程池,不再接收新的线程,未执行完的线程不会被关闭
        es.shutdown();
    }

}

继承thread
  优点:可以直接使用thread类中的方法,代码简单
  缺点:继承thread类之后就不能继承其他的类
实现runnable接口
  优点:即时自定义类已经有父类了也不受影响,因为可以实现多个接口
  缺点: 在run方法内部需要获取到当前线程的thread对象后才能使用thread中的方法
实现callable接口
  优点:可以获取返回值,可以抛出异常
  缺点:代码编写较为复杂

package com.wpbxx.test;

import java.util.concurrent.callable;
import java.util.concurrent.executionexception;
import java.util.concurrent.executorservice;
import java.util.concurrent.executors;
import java.util.concurrent.future;

//简易写法  使用匿名内部类创建多线程


public class helloworld {

    public static void main(string[] args) throws interruptedexception, executionexception {
        new thread() {
            public void run() {
                for(int i = 0; i < 1000; i++) {
                    system.out.println("wpb");
                }
            }
        }.start();
        
        new thread(new runnable() {
            public void run() {
                for(int i = 0; i< 1000; i++) {
                    system.out.println("xx");
                }
            }
        }).start();
        
        executorservice exec = executors.newcachedthreadpool();
        future<integer> result = exec.submit(new callable<integer>() {
            
        @override
        public integer call() throws exception{
            return 1024;
        }
        });
        
        system.out.println(result.get());
    }

}

thread设置线程的名字

方法一

new thread("马化腾") {                            //通过构造方法给name赋值
            public void run() {
                system.out.println("我是" + this.getname() + ",来腾讯工作吧 ");
            }
        }.start();

方法二

new thread() {
            public void run() {
                this.setname("马化腾");      //调用setname
                system.out.println("我是" + this.getname() + ",来腾讯啊");
            }
        }.start();

使用thread.currentthread() 获得正在运行的线程

可以这样改变runnable中线程名字

package com.wpbxx.test;

public class helloworld {

    public static void main(string[] args) {
      new thread(new runnable() {
          public void run() {
          system.out.println(thread.currentthread().getname());
          thread.currentthread().setname("wpb");
          system.out.println(thread.currentthread().getname());
          }
      }).start();
    }

}

线程睡眠

thread中的sleep方法可以使当前线程睡眠,线程睡眠后,里面的任务不会执行,待睡眠时间过后会自动苏醒,从而继续执行任务。

thread.sleep(1000);   //让当前线程睡眠1秒

线程的优先级

setpriority()方法接收一个int类型的参数,通过这个参数可以指定线程的优先级,取值范围是整数1~10,优先级随着数字的增大而增强。  但并不是一定执行优先级高的执行完之后  才执行别的

package com.wpbxx.test;

public class helloworld {

    public static void main(string[] args) {
        thread t1 = new thread() {
            public void run() {
                for(int i = 0; i<100; i++) {
                    system.out.println("wpb");
                }
            }
        };
        
        thread t2 = new thread() {
            public void run() {
                for(int i = 0; i < 100; i++) {
                    system.out.println("1024");
                }
            }
        };
        
        t1.setpriority(10);
        t2.setpriority(0);
        t1.start();
        t2.start();
        
        
    }

}

唤醒睡眠中的线程

  t1.interrupt();

用interrupt方法会抛出一个interruptedexception的异常。

 

同步方法

package com.wpbxx.test;

public class helloworld {

    public static void main(string[] args) {
    
        task tk = new task();
        
        thread t1 = new thread() {
            public void run() {
                tk.changenum(true);
            }
        };
        thread t2 = new thread() {
            public void run() {
                tk.changenum(false);
            }
        };
        t1.start();
        t2.start();
    }

}
class task{
    
    private int num = 0;
    public void changenum(boolean flag) {
        if(flag) {
            num = 99;
            system.out.println(thread.currentthread().getname() + "-------" + "begin");
            system.out.println(thread.currentthread().getname() + "-------" + num);
            system.out.println(thread.currentthread().getname() + "-------" + "end");
        }else {
            num = 22;
            system.out.println(thread.currentthread().getname() + "-------" + "begin");
            system.out.println(thread.currentthread().getname() + "-------" + num);
            system.out.println(thread.currentthread().getname() + "-------" + "end");
        }
    }
}

正常情况下应该打印出一个88一个66,可是上面却两个线程打印出的两个66,这样就出现了线程安全的问题,出现这个问题的原因是成员变量存储在堆内存中,两个线程共享堆内存,即两个线程可以对同一个num进行修改。
程序执行分析:
cpu执行t1线程,将num修改为88,之后cpu开始执行t2线程,将num修改为66,打印出66,cpu开始执行t1线程,打印num的值,此时num的值是66。

 

在方法上加入synchronized关键字,这样在执行多个线程时看哪个线程先执行这个方法,假设有t1,t2,t3三个线程中都调用了changenum方法,t1线程先执行了这个方法,那么t1会先在task对象上面加锁,加锁后,别的线程就无法执行当前task对象上的changenum方法,直到t1执行结束changenum方法之后,t2,t3中的一个线程才可以执行这个方法,这就保证了在某个时间段内只有一个线程执行changenum方法,解决了线程安全问题。
注意:synchronized锁住的是当前对象,如果t1线程和t2线程里面是不同的对象,则不需要同步,因为不会发生线程安全问题

 

public synchronized void changenum(boolean flag)  加这一句就ok了


也可以对需要互斥访问的代码块加上synchronized
    public void changenum(boolean flag){

        try {
            thread.sleep(3000);
            system.out.println("执行一个耗时较长的任务");
        } catch (interruptedexception e) {
            e.printstacktrace();
        }

        //这个方法中,需要同步的代码块是这部分,而上面耗时操作的代码,不涉及到线程安全问题,所以不需要同步
        synchronized(obj){
            if(flag){
                num = 88;

                system.out.println(thread.currentthread().getname() + "========== begin");
                system.out.println(thread.currentthread().getname() + "==========" + num);
                system.out.println(thread.currentthread().getname() + "========== end");
            }else{
                num = 66;

                system.out.println(thread.currentthread().getname() + "========== begin");
                system.out.println(thread.currentthread().getname() + "==========" + num);
                system.out.println(thread.currentthread().getname() + "========== end");
            }
        }

    }

死锁

发生死锁原因就是两个或多个线程都在等待对方释放锁导致,下面通过代码来演示一下死锁情况。

package com.wpbxx.test;

public class helloworld {

    private static object obj1 = new object();
    private static object obj2 = new object();
    public static void main(string[] args) {
        new thread() {
            public void run() {
                synchronized(obj1) {
                    system.out.println(this.getname());
                    
                    synchronized(obj2) {
                        system.out.println(this.getname());
                    }
                }
            }
        }.start();
         new thread() {
            public void run() {
                synchronized(obj2) {
                    system.out.println(this.getname());
                    
                    synchronized(obj1) {
                        system.out.println(this.getname());
                    }
                }
            }
        }.start();
    }
}

 

volatile关键字

package com.wpbxx.test;

public class helloworld {

    public static void main(string[] args) throws interruptedexception {
        
        task task = new task();
        
        thread t1 = new thread(task);
        
        t1.start();
        
        thread.sleep(100);
        task.setflag(false);
    }
}
class task implements runnable{
    
    private boolean flag = true;
    
    public boolean isflag() {
        return flag;
    }
    
    public void setflag(boolean flag) {
        this.flag = flag;
    }
    
    public void run() {
        while(flag) {
            system.out.println("while循环");
        }
        system.out.println("循环结束");
    }
}

上面程序中在64位的机器上以server模式运行时,有可能会出现死循环的现象。

jvm的运行可以分为下面两种模式:

  • client:启动快,运行后性能不如server模式,一般运行时默认是client模式
  • server:启动慢,运行后性能比client模式好。

在eclipse中可以通过配置来使用server模式,右键—>run as—>run configurations。写上-server。然后点击run即可

上面程序出现问题的原因这样的,虽然在主线程中将flag的设置为false,但是jvm为了提升效率,t1线程一直在私有内存中获取flag的值,而私有内存中的flag值并没有被改变,所以导致死循环的发生。

使用volatile修饰flag解决上面问题:

volatile private boolean flag = true;

将flag声明为volatile后,t1线程会从公共的内存中访问flag的值,这样在主线程将flag设置为false后,t1线程中的循环就会结束了。

注意:volatile只能修饰变量,不能修饰方法

原子性和非原子性

原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

非原子性:不符合原子性的就是非原子性

  int x = 1024; //原子性

    int y = x; //cpu先去内存中读取x的值,读取后在为y进行赋值,在读取后给y赋值前的这段时间可能会切换到其他线程上面。

    x++; //包含了三个操作,先读取x的值,然后进行加1操作,最后写入新的值,在这三个操作的间隙可能会切换到其他线程上面。

    x = x + 1; //同上

volatile是非原子性的。
synchronized是原子性的。

 timertask

timertask是一个实现了runnable接口的抽象类,需要编写一个类继承timertask类,将要在定时任务执行的代码编写在run方法中。

要想执行定时任务,需要创建timer的对象并调用里面的schedule方法,在timer类中有多个重载的schedule方法,这里咱们使用这个:

schedule(timertask task, date firsttime, long period);

package com.wpbxx.test;

import java.text.parseexception;
import java.text.simpledateformat;
import java.util.timer;
import java.util.timertask;

public class helloworld {

    public static void main(string[] args) throws interruptedexception, parseexception {
        
        timer t = new timer();
        t.schedule(new mytimertask(), new simpledateformat("yyyy-mm-dd hh:mm:ss sss").parse("2017-07-03 18:09:00 000"),
                5000);
    }
}
class mytimertask extends timertask{
    @override
    public void run() {
        system.out.println("wpbxx");
    }
}

 

 

线程之间的通信

多线程环境下cpu会随机的在线程之间进行切换,如果想让两个线程有规律的去执行,那就需要两个线程之间进行通信,在object类中的两个方法wait和notify可以实现通信。

wait方法可以使当前线程进入到等待状态,在没有被唤醒的情况下,线程会一直保持等待状态。
notify方法可以随机唤醒单个在等待状态下的线程

 

利用wait  和notify  进行交替打印

package com.wpbxx.test;

import java.text.parseexception;
import java.text.simpledateformat;
import java.util.timer;
import java.util.timertask;

public class helloworld {

    public static void main(string[] args) throws interruptedexception, parseexception {
        print p = new print();
        thread t1 = new thread() {
            public void run() {
                while(true) {
                    p.print1();
                }
            }
        };
        thread t2 = new thread() {
            public void run() {
                while(true) {
                    p.print2();
                }
            }
        };
        t1.start();
        t2.start();
    }
}
class print{
    private int flag = 1;
    
    public void print1() {
        synchronized(this) {
            if(flag != 1) {
                try {
                    this.wait();
                }catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }
            
            system.out.println("wpb");
            flag = 2;
            this.notify();
        }
    }
    public void print2() {
        synchronized(this) {
            if(flag != 2) {
                try {
                    this.wait();
                }catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }
            
            system.out.println("xx");
            flag = 1;
            this.notify();
        }
    }
}

 

但这样如果是三个线程以上的  就不行,  可能出现死锁了

因为是随机唤醒一个等待的线程,  假设一线程 进行玩后 随即唤醒一个线程,并把flag = 2,  但这时唤醒了线程3  就会一直等待

notifyall()  为唤醒所有的线程

java多线程相关代码
package com.wpbxx.test;

/**
 * 三个(三个以上)线程之间的通信
 *
 */
public class helloworld {

    public static void main(string[] args) {

        print1 p = new print1();

        thread t1 = new thread(){
            public void run(){
                while(true){
                    p.print1();
                }

            }
        };

        thread t2 = new thread(){
            public void run(){
                while(true){
                    p.print2();
                }
            }
        };

        thread t3 = new thread(){
            public void run(){
                while(true){
                    p.print3();
                }
            }
        };

        t1.start();
        t2.start();
        t3.start();
    }

}

class print1{

    private int flag = 1;

    public void print1(){
        synchronized(this){
            while(flag != 1){
                try {
                    //让当前线程进入等待状态
                    this.wait();
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }

            system.out.println("monkey");
            flag = 2;
            //唤醒所有等待的线程
            this.notifyall();
        }

    }

    public void print2(){
        synchronized(this){
            while(flag != 2){
                try {
                    this.wait();
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }

            system.out.println("1024");
            flag = 3;
            this.notifyall();
        }

    }

    public void print3(){
        synchronized(this){
            while(flag != 3){
                try {
                    this.wait();
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }

            system.out.println("888");
            flag = 1;
            this.notifyall();
        }

    }
}
view code

这样就可以实现三个线程的交替打印, 但会有问题  就是唤醒所有的线程  开销太大。

 

 

上面notify() 或者 notifyall()  并不能唤醒指定的线程,所以多出了  互斥锁

新增了  reentrantlock类 和    condition接口  来替换   synchronized关键字   和   wait、notify  方法。

reentrantlock类     和condition接口    都在java.util.concurrent.locks包下。
可以使用       reentrantlock类中    的  lock方法   和   unlock方法     进行上锁和解锁,用来替代synchronized关键字。
condition接口中的await方法和signal方法用来让线程等待和唤醒指定线程。用来替代wait方法和notify方法。

 

如 还是循环打印东西

package com.wpbxx.test;

import java.util.concurrent.locks.condition;
import java.util.concurrent.locks.reentrantlock;

public class helloworld {

    public static void main(string[] args) throws interruptedexception {
        
        print p = new print();
        thread t1 = new thread() {
            public void run() {
                while(true) {
                    p.print1();
                }
            }
        };
        thread t2 = new thread() {
            public void run() {
                while(true) {
                    p.print2();
                }
            }
        };
        thread t3 = new thread() {
            public void run() {
                while(true) {
                    p.print3();
                }
            }
        };
        t1.start();
        t2.start();
        t3.start();
    }
}
class print{
    private reentrantlock r = new reentrantlock();
    
    private condition c1 = r.newcondition();
    private condition c2 = r.newcondition();
    private condition c3 = r.newcondition();
    
    private int flag = 1;
    public void print1() {
        r.lock();
        
        while(flag != 1) {
            try {
                c1.await();
            } catch (interruptedexception e) {
                // todo auto-generated catch block
                e.printstacktrace();
            }
        }
        system.out.println("wpb1");
        flag = 2;
        c2.signal();
        r.unlock();
    }
    
    public void print2() {
        r.lock();
        
        while(flag != 2) {
            try {
                c2.await();
            } catch (interruptedexception e) {
                // todo auto-generated catch block
                e.printstacktrace();
            }
        }
        system.out.println("wpb2");
        flag = 3;
        c3.signal();
        r.unlock();
    }
    public void print3() {
        r.lock();
        
        while(flag != 3) {
            try {
                c3.await();
            } catch (interruptedexception e) {
                // todo auto-generated catch block
                e.printstacktrace();
            }
        }
        system.out.println("wpb3");
        flag = 1;
        c1.signal();
        r.unlock();
    }
    
    
}

以后再补充