java线程与进程

时间:2023-02-15 00:04:43

      Java线程与进程

进程与线程的关系

  进程里面至少有一个线程,进程间的切换会有较大的开销

  线程必须依附在进程上,同一进程共享代码和数据空间

多线程的优势

  多线程可以达到高效并充分利用cpu

线程使用的方法

继承Thread类使用线程

public class Test{
    public static void main(String[] args){
        MyThread t = new MyThread("x1x");
        t.start();      // 启动线程
    }
}

// 自定义一个类,继承Thread类,重写run()方法
class MyThread extends Thread{
    private Thread thread;
    private String threadName;

    public MyThread(String threadName){
        this.threadName = threadName;
    }

    @override
    public void run(){
        System.out.println("线程名称:" + threadName);
        for(int i = 0; i < 5; i++){
            System.out.println("Thread:" + threadName + "-----" + i);
        }
    }

    public void start(){
        if(null == thread){
            thread = new Thread(this, threadName);
            thread.start();
        }
    }
}    

继承Thread类

实现Runnable接口使用线程 

 public class Test{
     public static void main(String[] args){
         MyRunnable m = new MyRunnable("r1r");
         m.start();
     }
 }

 // 实现Runnable接口,实现run()方法
 class MyRunnable implements Runnable{
     private Thread thread;
     private String threadName;
     // 构造方法
     public MyRunnable(String threadName){
         this.threadName = threadName;
     }

     @override
     public void run(){
         System.out.println("线程名称: " + threadName);
         for(int i = 0; i < 6; i++){
             System.out.println("Thread: " + threadName + "----" + i);
         }
     }

     public void start(){
         if(null == thread){
             thread = new Thread(this, threadName);
             thread.start();
         }
     }
 }

实现Runnable接口

实现Callable接口使用线程

 public class MyCallable implements Callable<Integer>{
     public static void main(String[] args){
         MyCallable mc = new MyCallable();
         // 这里的泛型里面填写的类型也与实现Callable时填写的一致
         FutureTask<Integer> fTask = new FutureTask<>(mc);
         new Thread(fTask, "带返回值的线程").start();
         System.out.println(fTask.get());    // 拿到返回值
     }

     /*
      * call()的返回值类型和实现Callable时填写的类型一致,比如这里是Integer类型,那么
      * call()方法的返回值类型就是Integer类型
     */
     @override
     public Integer  call() throws Exception{
         for(int i = 0; i < 100; i++){
             System.out.println(Thread.currentThread().getName() + "----" + i);
         }
         return i;
     }
 }

实现Callable接口

用匿名内部类的形式使用线程

 public class Test{
     public static void main(String[] args){
         // 利用Thread类实现
         new Thread(){
             @override
             public void run(){
                 while(true){
                     System.out.println(Thread.currentThread().getName());
                 }
             }
         }.start();

         // 利用Runnable接口实现
         new Thread(new Runnable(){
             @override
             public void run(){
                 while(true){
                     System.out.println(Thread.currentThread().getName());
                 }
             }
         }).start();
     }
 }

内部类形式调用

继承Runnable和Callable接口比继承Thread类的优势

  • 适合多个相同的程序代码的线程去处理同一个资源
  • 可以避免java中的单继承的限制
  • 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

线程的优先级

java中的线程是具有优先级的,优先级高的线程会获得更多的运行机会。

java中线程的优先级是用整数来表示的,取值范围是1 - 10

Thread类中有三个静态常量表示优先级:

  static int MAX_PRIORITY   线程可以具有的最高优先级,取值为10

  static int MIN_PRIORITY    线程可以具有的最低优先级,取值为1

  static int NORM_PRIORITY  分配给线程的默认优先级,取值为5

如果要对线程设置优先级或者获取线程的优先级,可以使用Thread类的setPriority 和 getPriority方法分别得到和获取

线程的常用方法

  sleep()     强迫线程睡眠

  join()        等待线程终止

  currentThread()    得到当前方法所在的线程

  isDaemon()    一个线程是否为守护线程

  setDaemon()    设置一个线程为守护线程

  setName()      为线程设置名称

  wait()      强迫线程等待

  interrupt()    终止线程

  notify()  notifyAll()   通知线程继续执行

  setPriority()     设置优先级

  getPriority()     获取优先级

同步

使用synchronized同步锁

  1. 获得同步锁
  2. 清空工作内存
  3. 从主内存拷贝对象到工作内存
  4. 刷新主内存
  5. 释放同步锁
 /*
  * 这个程序的本意是第一个线程先输出zhangsan,然后第二个线程输出lisi
  * 但是由于两个线程同步执行,第一个线程调用output()方法还未结束,第二个线程就已
  *经开始调用output()方法
  * 最终的输出就会是zlhiasnigsan
  * 就好比两个工人在同一时间使用同一机器加工零件,那不就乱套了吗?
  */

 public class synchronizedTest {
     public static void main(String[] args) {
         final Outputter o = new Outputter();
         new Thread() {
             public void run() {
                 o.output("zhangsan");
             }
         }.start();
         new Thread(new Runnable() {

             @Override
             public void run() {
                 // TODO Auto-generated method stub
                 o.output("lisi");
             }
         }).start();
     }
 }

 class Outputter{
     // 放在代码块前面
     public void output(String name) {
         for(int i = 0; i < name.length(); i++) {
             System.out.print(name.charAt(i));
             try {
                 Thread.sleep(1000);
             } catch (InterruptedException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
         }
     }
 }

没有同步带来的问题

 /*
  * 在要调用的output()方法前面加上synchronized同步锁就可以解决上述问题
  * 当使用同步锁后,同一对象在同一时间只允许一个线程使用
  * 也就是说,第一个线程调用output()方法,在进入方法后,synchronized就
  * 上锁了,后面的线程就不能调用了,必须等待当前线程执行完output()方法释放锁后
  * 后面的线程才允许调用
  * 就好比第一个工人要开始使用机器加工零件了,在使用机器前,他将房门锁住了,于是
  * 后面
  * 的工人便不能进房间使用机器,必须等工人一加工完零件,将锁打开,第二个工人才能
  * 进去
  * 加工零件
  */

 public class synchronizedTest {
     public static void main(String[] args) {
         final Outputter o = new Outputter();
         new Thread() {
             public void run() {
                 o.output("zhangsan");
             }
         }.start();
         new Thread(new Runnable() {

             @Override
             public void run() {
                 // TODO Auto-generated method stub
                 o.output("lisi");
             }
         }).start();
     }
 }

 class Outputter{
     // 放在代码块前面
     public synchronized void output(String name) {
         for(int i = 0; i < name.length(); i++) {
             System.out.print(name.charAt(i));
             try {
                 Thread.sleep(1000);
             } catch (InterruptedException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
         }
     }
 }

使用同步解决问题1

 /*
  * 第二种使用synchronized的方法就是将synchronized
  * 放在要上锁的代码块前面,原理与上述一致
  */

 public class synchronizedTest {
     public static void main(String[] args) {
         final Outputter o = new Outputter();
         new Thread() {
             public void run() {
                 o.output("zhangsan");
             }
         }.start();
         new Thread(new Runnable() {

             @Override
             public void run() {
                 // TODO Auto-generated method stub
                 o.output("lisi");
             }
         }).start();
     }
 }

 class Outputter{
     public void output(String name) {
         // 放在代码块前面
         synchronized(this) {
             for(int i = 0; i < name.length(); i++) {
                 System.out.print(name.charAt(i));
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                 }
             }
         }
     }
 }

使用同步锁解决问题2

synchronized(){

}的括号里面放的是需要互斥的对象,就是两个工人加工零件使用的同一个机器

使用volatile同步变量

volatile使变量对线程具有可见性

java线程与进程