12.线程

package com.atguigu.java;

/*
    多线程的创建: 方式一:继承于Thread类
    ① 创建一个继承thread类的子类
    ② 重写Thread类的run()-->此线程执行的操作声明在run鸿
    ③创建Thread类的子类对象
    ④ 通过此对象调用start()
         start()的作用:①启动当前线程 ②调用当前线性run()
注意:①:不能通过直接调用run()的方式启动线程
     ② 当再启动一个线程,不可以还让已经start()的线程去执行,会报illegalThreadStateException
           需要新建一个线程对象

    例子:遍历100以内的多有偶数
 */
public class ThreadTest {
    public static void main(String[] args) {
        MyThread mythread = new MyThread();
        mythread.start();
        System.out.println("hello");
    }
}

//① 创建一个继承thread类的子类
class MyThread extends Thread{
   // ② 重写Thread类的run()
    @Override
    public void run() {
        for( int i = 0; i<=100; i++){
            if (i%2 == 0){
                System.out.println(i);
            }
        }
    }
}

 线程中常用方法

package com.atguigu.java;
/*
       测试Thread中的常用方法
       1.start():启动当前线程;调用当前线程run()方法
       2.run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
       3.currentThread(): 静态方法,返回执行当前代码的线程
       4.getName(): 获取当前线程的名字
       5.setName(): 设置当前线程的名字
       6.yield():释放当前cpu的执行权
       7.join():在线程a中调用线程b的join()方法,此时线程a就进入阻塞状态,当线性b执行完后,a开始执行
       8.sleep(long millitime):让当前线程睡眠指定millitime毫秒,在时间段内,当前线程是阻塞状态
       9.isAlive():判断当前线程是否存活

       线程的优先级
       1.  MAX_PRIORITY:10
           NORM_PRIORITY: 5 --normal 默认优先级
           MIN_PRIORITY: 1
       2.获取和设置当前线程的优先级
          getPriority():获取线程优先级
          setPriority(int p):设置线程优先级

        说明:高优先级的线程要抢占低优先级线程cpu的执行权,但并不意味着,只有当高优先级线程执行完之后,低优先级线程在执行

 */
public class ThreadMethodsTest {
    public static void main(String[] args) {
        Hthread h1 = new Hthread("thread:1-");
        //分线程起名:使用setName方法;或使用 带参构造器

        //设置分线程的优先级
        h1.setPriority(Thread.MAX_PRIORITY);

        //h1.setName("线程一");
        h1.start();
        //给主线程起名
        Thread.currentThread().setName("主线程");
        for(int i = 0; i <= 100;i++){
            if(i% 2 == 0){
                System.out.println(Thread.currentThread().getName() +i);
            }
            if (i == 20){
                try {
                    h1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(h1.isAlive());//h1已执行完 false
    }
}
class Hthread extends Thread{

    public Hthread(String name){
        super(name);
    }
    @Override
    public void run() {
        for(int i = 0; i <= 100;i++){
            if(i% 2 == 0){
                try {
                    sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() +i);
                System.out.println(Thread.currentThread().getPriority());
            }
            if(i % 20 == 0){
                this.yield();
            }
        }
    }
}
package com.atguigu.java;
/*
    例子:创建三个窗口,总票数为100张
    存在线程安全问题,待解决
 */
class Window extends Thread{
    private static int ticket = 100;
    @Override
    public void run() {
        while(true){
            if (ticket > 0 ){
                System.out.println(getName()+": 卖票,票号为:"+ticket);
                ticket--;
            }else{
                break;
            }
        }
    }
}
public class WindowTest {
    public static void main(String[] args) {
        Window w1 = new Window();
        Window w2 = new Window();
        Window w3 = new Window();
        w1.setName("窗口1");
        w2.setName("窗口2");
        w3.setName("窗口3");
        w1.start();
        w2.start();
        w3.start();
    }
}

创建线程方式二

package com.atguigu.java;
/*
     创建多线程的方式二:实现Runnable接口
        1.创建实现Runnable接口的类
        2.实现类去实现Runnable中的抽象方法:run()
        3.创建实现类的对象
        4.将此对象作为参数传递到Thread类的构造其中,创建Thread类的对象
        5.通过Thread类的对象调用start()

比较创建线程的两种方式:
开发中,优先选择 实现Runnable接口的方式
1.实现的方式没有类的单继承的局限性
2.实现类的方式更适合处理多个线程有共享数据的情况

联系;Thread类实现类Runnable接口
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run中
*/ public class ThreadTest1 { public static void main(String[] args) { Mthread m1 = new Mthread(); Thread t1 = new Thread(m1); Thread t2 = new Thread(m1); t1.start(); t2.start(); } } class Mthread implements Runnable{ @Override public void run() { for (int i = 0; i<= 100 ; i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }
package com.atguigu.java;
/*
    例子:创建三个窗口,总票数为100张

 */
public class WindowTest1 {
    public static void main(String[] args) {
        Windows w1 = new Windows();//window只具有一个对象,及共享一百张票
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.setName("窗口1:");
        t2.setName("窗口2:");
        t3.setName("窗口3:");
        t1.start();
        t2.start();
        t3.start();
    }

}
class Windows implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {
       while(true){
           if(ticket>0) {
               System.out.println(Thread.currentThread().getName() + ":" + ticket);
               ticket--;
           }else{
               break;
           }
       }
    }
}
Java中,通过同步机制,来解决线程安全问题
package com.atgugu.java;
/*
    例子:创建三个窗口,总票数为100张
    1.买票过程中,出现重票和错票
    2.问题出现原因,当某个线程操作过程中,且尚未完成,其它线程进行操作
    3.当一个线程在操作共享数据的时候,其他线程不可参与操作,指导当前线程操作完,其它线程才可以操作共享线程,即使当前线程阻塞,也不可改变
    4.通过同步机制,解决线程安全问题

    方式一:同步代码块
    synchronized(同步监视器){
        //需要被同步的代码
    }
    说明:1.操作共享数据的代码,即为需要被同步的代码
         2.共同数据:多个线程共同操作的变量。
         3.同步监视器(锁):任何一个类的对象,都可以充当一个锁,
               要求:多个线程必须要共用同一把锁。
         4.好处:同步代码块,解决了线程安全问题
           局限性:操作同步代码时,只能有一个线程参与,其它线程等待。想相当于单线程的过程
 */
public class WindowTest1 {
    public static void main(String[] args) {
        Windows w1 = new Windows();//window只具有一个对象,及共享一百张票
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.setName("窗口1:");
        t2.setName("窗口2:");
        t3.setName("窗口3:");
        t1.start();
        t2.start();
        t3.start();
    }

}
class Windows implements Runnable{
    private int ticket = 100;
    //Object obj = new Object();

    @Override
    public void run() {
       while(true){
           synchronized(this){//synchronized(obj){  优化:this当前对象-->Windows的对象--->只有一个对象w

               if(ticket>0) {
                   try {
                       Thread.sleep(100);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread().getName() + ":" + ticket);
                   ticket--;
               }else{
                   break;
               }
           }

       }
    }
}

 

package com.atgugu.java;
/*
     使用同步方法解决 实现Runnable接口的线程安全问题
     关于同步方法的总结:
     1.同步方法仍然涉及到同步监视器,知识不需要显示的声明
     2.非静态的同步方法,同步监视器是:this
         静态的同步方法,同步监视器是:当前类本身
 */
public class WindowTest2 {
    public static void main(String[] args) {
        Windows2 w1 = new Windows2();//window只具有一个对象,及共享一百张票
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.setName("窗口1:");
        t2.setName("窗口2:");
        t3.setName("窗口3:");
        t1.start();
        t2.start();
        t3.start();
    }

}
class Windows2 implements Runnable{
    private int ticket = 100;
    //Object obj = new Object();

    @Override
    public void run() {
        while(true){
            show();
        }
    }
    private synchronized void show(){//同步监视器:this
        if(ticket>0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":" + ticket);
            ticket--;
        }
    }
}
package com.atgugu.java1;
/*
     使用同步机制将单例模式中的懒汉式改写为线程安全的
 */
public class BlankTest {
    public static void main(String[] args) {
        Blank b1 = Blank.getBlank();
        Blank b2 = Blank.getBlank();
        System.out.println(b1 == b2);
    }
}
class Blank{
    private Blank(){
    }
    private static Blank blank = null;
    public synchronized static Blank getBlank(){
        if(blank == null){
            blank = new Blank();
        }
        return blank;
    }
    /*或者使用同步代码块
    public static Blank getBlank(){
        synchronized(Blank.class){
            if(blank == null){
                blank = new Blank();
            }
            return blank;
        }
    }*/
}
  死锁问题
package com.atgugu.java1;
/*
  1.死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了死锁
  2.说明:
       1)出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
       2)使用同步时,需要避免出现死锁
 */
public class ThreadTest {
    public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();

        new Thread(){
            @Override
            public void run() {
                synchronized(s1){
                    s1.append("a");
                    s2.append("1");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s2){
                        s1.append("b");
                        s2.append("2");
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(s2){
                    s1.append("c");
                    s2.append("3");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s1){
                        s1.append("d");
                        s2.append("4");
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();
    }
}

 解决线程安全方式三:lock锁
package com.atgugu.java1;

import java.util.concurrent.locks.ReentrantLock;

/*
   解决线程安全问题的方式三: lock锁  ---JDK5.0新增

   synchronized 与Lock的异同
   1.相同: 解决线程安全问题
   2.不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
          Lock需要手动的启动同步Lock(),同时结束同步也需要手动实现 unLock()
 */
public class LockTest {
    public static void main(String[] args) {
        Window w1 = new Window();
        Thread t1 = new Thread(w1);
        Thread t2 = new Thread(w1);
        Thread t3 = new Thread(w1);
        t1.start();
        t2.start();
        t3.start();
    }
}
class Window implements Runnable{
    private int ticket = 100;
    //1.实例化ReentrantLock
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            try{
                //2.调用锁定方法:lock()方法
                lock.lock();
                if (ticket >0 ){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+ticket);
                    ticket --;
                }else{
                    break;
                }
            }finally{
                //3.调用解锁方法:unlock()
                lock.unlock();
            }

        }
    }
}
线程通信的三个方法:
package com.atgugu.exec;
/*
   两个线程交替打印 1-100的数
   涉及到的三个方法:
   wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器。
   notify():一旦执行此方法,就会唤醒被wait()的一个线程,要是多个线程,按照优先级唤醒
   notifyall();一定那执行此方法,就会唤醒所有被wait的线程

   说明:
   1.wait、notify、notifyall:必须使用在同步代码块或同步方法中。
   2.wait、notify\notifyall: 调用者必须是同步代码块或者同步方法中的同步监视器,否则出现IllegalMonitorStateException
   3.wait、notify\notifyall: 定义在Object
 */
class Number implements Runnable{
    private int num = 1;
    private Object obj = new Object();
    @Override
    public void run() {
        while(true){
            synchronized(obj){
                obj.notify();//notify唤醒一个,notifyall 唤醒所有线程
                if (num <= 100){
                    try {
                        Thread.sleep(100);//进入阻塞状态,但不释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+":"+num);

                    try {
                        obj.wait();//使得调用如下wait方法的线程进入阻塞状态,并释放锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    break;
                }
            }

        }
    }
}
public class NumberTest {
    public static void main(String[] args) {

    }
}
 线程的创建方式三:实现Callable接口
package com.atgugu.java2;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/*
     创建线程的方式三:实现Callable接口 ---JDK5.0 新增加
 如何理解实现Callable接口比是心啊Runnable接口强大:
     1.call方法可以有返回值
     2.call方法可以抛出异常,被外面操作捕获,获取异常信息
     3.Callable可以支持泛型

 */
//1.创建一个实现Callable接口的实现类
class NumThread implements Callable{
    //2.实现call方法,将此线程需要执行的操作,放在call方法中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 1; i<=100; i++){
            if (i%2==0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}
public class ThreadNew {
    public static void main(String[] args) {
        //3.创建Callable接口实现类的对象
        NumThread n1 = new NumThread();
        //4.将次Callable接口实现类的对象床底到FutureTask的构造器中,创建FutureTask对象
        FutureTask f1 = new FutureTask(n1);
        //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start方法
        Thread t1 = new Thread(f1);
        t1.start();
        try {
            //6.(可选)获取Callable中call方法的返回值
            //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值
            Object sum = f1.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
线程的创建方式四:使用线程池
package com.atgugu.java2;
/*
     创建线程的方式4:使用线程池
 使用连接池的好处
     1.提高响应速度(减少了创建新线程的时间)
     2.降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
     3.便于线程管理
        corePoolSize 核心池的大小
        maximumPoolSize:最大线程数
        keepAliveTime:线程没有任务时最多保持多长时间后会终止
     
 */

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class NumberThread implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i<=100; i++){
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}
class NumberThread1 implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i<=100; i++){
            if (i%2 != 0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        //1.提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        //2.执行指定的线程操作,需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适合使用于Runnable
        service.execute(new NumberThread1());//适合使用于Runnable
        //service.submit();适合使用于Callable
        //3.关闭线程池。
        service.shutdown();
    }
}
 
posted @ 2020-07-02 22:58  孫sun  阅读(139)  评论(0编辑  收藏  举报